From: Tim M. <tim...@po...> - 2003-04-08 01:13:05
|
Guys - The recent rash of cvs commits are an attempt by Nat and I (and Steve too) to get a version of the java dynamic mock implementation in a state that it supports the creation of simple, easily readable tests, but at the same time supports more complicated constraint based calls for the rarer cases that need something extra. The followig is a bit of a ramble (or brain dump) - but i thought its probably best to dump away so people know whats going on (and can try it out. I want to give it a spin for a few days and see how it feels to). As we all know, supporting hard coded mocks is a bit of pain (sometimes - as with the file io stuff - there isn't any simple way... although the work on NMock and rupert's team gives some other possibilities with class loader method replacements). In doing this work we generated some hard mocks and noted that we need to sort out MockMakers' reliance on things that aren't in the core library (or are, but are called something different). For clean, TDD code - dynamic mocks give a nice TDD feel if there isn't too much overhead in writing the tests. The previous implementation established the usefulness of a dynamic approach but it was a bit awkward to use in practice and fixing some of its defficiencies were tricky. The aim of this new branch is too: - make writing normal tests as easy as possible (see the acceptance test below). The discussion when with a partner should be natural with not too much initial complexity. - make failed mock tests break cleanly and clearly with good error messages (the classic library did a reasonably good job of this, but had no tests to show it) - provide a tested framework that will support modifications (e.g. eat our own dogfood) - allow support for more aesoteric<sp?> call orderings. e.g. *Sequence*Bag* Our test functional test was to write a servlet test that could look like this: public void testDoGetNew() throws ServletException, IOException { Mock mockHttpServletResponse = new Mock(HttpServletResponse.class, "response"); Mock mockHttpServletRequest = new Mock(HttpServletRequest.class); // name optional mockHttpServletRequest.expectAndReturn( "getParameter", C.args(C.eq("subject")), "Mail Subject" ); mockHttpServletRequest.expectAndReturn( "getParameter", C.args(C.eq("body")), "Mail Body" ); mockHttpServletRequest.permitAndReturn( "getParameter", C.args(C.eq("browser-identifier")), "MSIE-5.0" ); final StringWriter contentWriter = new StringWriter(); // Not yet implemented - proposed way of introducing a sequence with not to much syntax // CallSequence seq = mockHttpServletResponse.expectSequence(); // seq.expectVoid( "setContentType", C.args(C.eq("text/html")) ); // seq.expectAndReturn( "getWriter", C.args(), contentWriter ); SimpleServlet aServlet = new SimpleServlet(); aServlet.doGet( (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy()); mockHttpServletRequest.verify(); mockHttpServletResponse.verify(); } In the branch we haven't got ordered sequences working yet (it should be simple - a proposed syntax is shown. We want something that isn't too heavy wait and works will with TDD). In eclipse - to find the branch search for tags on the Mock class (e.g. tag is DynamicMockExperiment). Some notes: a mock defaults to a type of CallSet (we can't agree on this - tim thinks it has a type of CallSet). Either way, there is a primitive add method that has CallMatch and ExpectedCall objects (that decorate each other) to give the symantics we want. There is no CallSequence yet, adding it should give a CallCollection super class. We haven't yet got a SingleCall decoration to limit a CallMatch to a single invocation (again should be easy). CallSet has a DecoratorFactory so we have a hook to test the syntactic sugar for expectAndReturn (by the way - permitAndReturn is a way of stubbing without an expectation, permit because the return is based on a call that conforms to the constraints). Everything conforms to CallMocker (an awful name - can't think of a good one). Having this interface meant we could use mockmaker to generate hard mocks to allow us to test the implementation. We didn't do this soon enough (thinking it was too meta circular, but if you don't mix dynamic and hard mocks it works out well and it simplified a lot of our testing. Not all the names are correct, and some of the tests could do with a partner for Nat or I to work with to cause a cleanup (anyone up for remote pairing?) I think thats it for now. Tim --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.467 / Virus Database: 266 - Release Date: 01/04/2003 |
From: Nat P. <nat...@b1...> - 2003-04-08 10:28:56
|
Here's some more info about the new branch, and how it improves the old design, and some thoughts on naming. Mock now inherits from CallSet (bad name,see below) and, when CallSequence is also implemented, will inherit from CallCollection. Mock adds the dynamic proxying to a collection of calls. The CallCollection base class will hold the implementation of the syntactic sugar, so new syntactic sugar will automagically be supported by CallSet, CallSequence and Mock objects. This removes a major deficiency in the previous design that duplicated the sugar methods in the Mock and CallSequence classes. We felt that the code was driving us to pull out a "Call" class that represented a call in progress. Call objects would store the method name, argument types, argument values and possible the receiver mock and proxy objects. Methods which now receive a method name and object array as arguments would be changed to receive a reference to a Call object. This would also make it easy to support overridden methods, which the current implementation doesn't yet do. Naming... Tim and I wrote this new version quite fast, and there were several times when we couldn't think of a good name for something and chose to press on with implementation and refactor later. So feel free to suggest new names for classes and/or methods. Currently the names "CallSet" and "CallMocker" make me feel particularly uncomfortable. CallSet does not implement either a mathematical set or a mathematical bag. Since it chooses CallMocker to call, I would now rename it to "CallSelector", which I think fits well with the name "CallSequence". I can't currently think of a good name for CallMocker, but when we implement the Call class a new name will be pretty urgent. Probably because of the meta nature of using mocks to test mocks that make calls to objects that mock calls, the code is currently full of classes with the works "call" and "mock" in them in various combinations and orders, which makes it confusing. The worst examples are where we had to use the MockCallMocker class to test CallMocker subclasses! Finally, because I'm currently in the middle of moving house (on to a boat actually) I will be disconnected for a while and won't be able to work on the new branch. After this weekend I'll have time to pair with somebody again, but I will not be able to do so at my place for a week or two. Regards, Nat. _______________________ Dr. Nathaniel Pryce B13media Ltd. http://www.b13media.com +44 (0)7712 526 661 ----- Original Message ----- From: "Tim Mackinnon" <tim...@po...> To: "Mockobjects-Java-Dev" <moc...@li...> Cc: <ch...@e2...>; "Paolo Polce" <pao...@bt...>; <ma...@co...>; "Ivan Moore" <iv...@ta...> Sent: Tuesday, April 08, 2003 2:16 AM Subject: [MO-java-dev] Dynamic Mock Updates > Guys - > > The recent rash of cvs commits are an attempt by Nat and I (and Steve too) > to get a version of the java dynamic mock implementation in a state that it > supports the creation of simple, easily readable tests, but at the same time > supports more complicated constraint based calls for the rarer cases that > need something extra. The followig is a bit of a ramble (or brain dump) - > but i thought its probably best to dump away so people know whats going on > (and can try it out. I want to give it a spin for a few days and see how it > feels to). > > As we all know, supporting hard coded mocks is a bit of pain (sometimes - as > with the file io stuff - there isn't any simple way... although the work on > NMock and rupert's team gives some other possibilities with class loader > method replacements). In doing this work we generated some hard mocks and > noted that we need to sort out MockMakers' reliance on things that aren't in > the core library (or are, but are called something different). > > For clean, TDD code - dynamic mocks give a nice TDD feel if there isn't too > much overhead in writing the tests. The previous implementation established > the usefulness of a dynamic approach but it was a bit awkward to use in > practice and fixing some of its defficiencies were tricky. > > The aim of this new branch is too: > - make writing normal tests as easy as possible (see the acceptance test > below). The discussion when with a partner should be natural with not too > much initial complexity. > - make failed mock tests break cleanly and clearly with good error messages > (the classic library did a reasonably good job of this, but had no tests to > show it) > - provide a tested framework that will support modifications (e.g. eat our > own dogfood) > - allow support for more aesoteric<sp?> call orderings. e.g. *Sequence*Bag* > > Our test functional test was to write a servlet test that could look like > this: > > public void testDoGetNew() throws ServletException, IOException { > Mock mockHttpServletResponse = new Mock(HttpServletResponse.class, > "response"); > Mock mockHttpServletRequest = new Mock(HttpServletRequest.class); // name > optional > > mockHttpServletRequest.expectAndReturn( > "getParameter", C.args(C.eq("subject")), "Mail Subject" ); > mockHttpServletRequest.expectAndReturn( > "getParameter", C.args(C.eq("body")), "Mail Body" ); > mockHttpServletRequest.permitAndReturn( > "getParameter", C.args(C.eq("browser-identifier")), "MSIE-5.0" ); > > final StringWriter contentWriter = new StringWriter(); > // Not yet implemented - proposed way of introducing a sequence with not to > much syntax > // CallSequence seq = mockHttpServletResponse.expectSequence(); > // seq.expectVoid( "setContentType", C.args(C.eq("text/html")) ); > // seq.expectAndReturn( "getWriter", C.args(), contentWriter ); > > SimpleServlet aServlet = new SimpleServlet(); > aServlet.doGet( > (HttpServletRequest) mockHttpServletRequest.proxy(), > (HttpServletResponse) mockHttpServletResponse.proxy()); > > mockHttpServletRequest.verify(); > mockHttpServletResponse.verify(); > } > > In the branch we haven't got ordered sequences working yet (it should be > simple - a proposed syntax is shown. We want something that isn't too heavy > wait and works will with TDD). In eclipse - to find the branch search for > tags on the Mock class (e.g. tag is DynamicMockExperiment). > > Some notes: > > a mock defaults to a type of CallSet (we can't agree on this - tim thinks it > has a type of CallSet). Either way, there is a primitive add method that has > CallMatch and ExpectedCall objects (that decorate each other) to give the > symantics we want. There is no CallSequence yet, adding it should give a > CallCollection super class. > > We haven't yet got a SingleCall decoration to limit a CallMatch to a single > invocation (again should be easy). CallSet has a DecoratorFactory so we have > a hook to test the syntactic sugar for expectAndReturn (by the way - > permitAndReturn is a way of stubbing without an expectation, permit because > the return is based on a call that conforms to the constraints). > > Everything conforms to CallMocker (an awful name - can't think of a good > one). Having this interface meant we could use mockmaker to generate hard > mocks to allow us to test the implementation. We didn't do this soon enough > (thinking it was too meta circular, but if you don't mix dynamic and hard > mocks it works out well and it simplified a lot of our testing. > > Not all the names are correct, and some of the tests could do with a partner > for Nat or I to work with to cause a cleanup (anyone up for remote pairing?) > > I think thats it for now. > > Tim > --- > Outgoing mail is certified Virus Free. > Checked by AVG anti-virus system (http://www.grisoft.com). > Version: 6.0.467 / Virus Database: 266 - Release Date: 01/04/2003 > > > > ------------------------------------------------------- > This SF.net email is sponsored by: ValueWeb: > Dedicated Hosting for just $79/mo with 500 GB of bandwidth! > No other company gives more support or power for your dedicated server > http://click.atdmt.com/AFF/go/sdnxxaff00300020aff/direct/01/ > _______________________________________________ > Mockobjects-java-dev mailing list > Moc...@li... > https://lists.sourceforge.net/lists/listinfo/mockobjects-java-dev |
From: Nat P. <nat...@b1...> - 2003-04-08 15:44:17
Attachments:
newmocks.gif
|
While supping a latte in a pavement cafe on Upper Street (ah, the joys of unemployment!) a particle of inspiration happened to penetrate my dense skull and hit a receptive spot within, sparking some thoughts about the new dynamic mock branch. I'm commiting them to email in case I forget them before I get a chance to implement them, and Cc'ing them to the mockobjects mailing list for review by everyone else. Firstly, I think that the "CallMocker" interface would be better renamed to "Callable", and "CallSet" would be better named "CallableSelection". Secondly, I have come to agree with your (Tim's) idea about separating the sugar methods from the composite callable classes. You're right -- it will makes the methods much easier to test. I also now think that the Mock class should *not* inherit from CallableSelection. By extending CallableSelection, Mock objects inherit the matches method, which is of no use to their clients. By separating the sugar methods from the composite implementations, and making the Mock class extend the sugar class, we get a much simpler design, make the sugar methods easier to test, remove the unnecessary matches method from the Mock class, and allow the user to choose whether a mock expects a selection or sequence of callables at construction time. A problem we had on Monday was how to make it easy for users to create composites *and* use sugar methods for those composites. The solution is for the sugar methods that create composites (expectSequence for example) to create and return a wrapper that provides sugar methods for the new composite, rather than return the composite itself. I've attached a quick UML class diagram that illustrates this. Apologies for the BDUF, but I have no easy access to CVS at the moment and so cannot implement it directly (and I would prefer to pair program it anyway -- perhaps at an XTC evening). I've called the sugar class "CallCollection", and the base interface of CallableSequence and CallableSelection, "CompositeCallable". CompositeCallable just adds the method "void add( Call call )" to the Callable interface. Use of the sugar methods would look the same as in your (Tim's) email but would always return a CallCollection rather than a CallSequence or CallSelection: Mock aMock = new Mock(HTTPServletResponse.class); CallCollection seq = aMock.expectSequence(); seq.expectVoid( "setContentType", C.args("text/html") ); CallCollection sel = seq.expectSelection(); // these headers must be set, but can be in any order sel.expectVoid("setHeader", C.args( C.eq("header1") ); sel.expectVoid("setHeader", C.args( C.eq("header2") ); sel.expectVoid("setHeader", C.args( C.eq("header3") ); // additional headers are optional sel.permitVoid("setHeader", C.args( C.IS_ANYTHING ) ); seq.expectAndReturn( "getWriter", C.args(), WRITER ); Cheers, Nat. _______________________ Dr. Nathaniel Pryce B13media Ltd. http://www.b13media.com +44 (0)7712 526 661 |