You can subscribe to this list here.
2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(13) |
Aug
(151) |
Sep
(21) |
Oct
(6) |
Nov
(70) |
Dec
(8) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2002 |
Jan
(47) |
Feb
(66) |
Mar
(23) |
Apr
(115) |
May
(24) |
Jun
(53) |
Jul
(10) |
Aug
(279) |
Sep
(84) |
Oct
(149) |
Nov
(138) |
Dec
(52) |
2003 |
Jan
(22) |
Feb
(20) |
Mar
(29) |
Apr
(106) |
May
(170) |
Jun
(122) |
Jul
(70) |
Aug
(64) |
Sep
(27) |
Oct
(71) |
Nov
(49) |
Dec
(9) |
2004 |
Jan
(7) |
Feb
(38) |
Mar
(3) |
Apr
(9) |
May
(22) |
Jun
(4) |
Jul
(1) |
Aug
(2) |
Sep
(2) |
Oct
|
Nov
(15) |
Dec
(2) |
2005 |
Jan
(1) |
Feb
(1) |
Mar
|
Apr
(1) |
May
(28) |
Jun
(3) |
Jul
(11) |
Aug
(5) |
Sep
(1) |
Oct
(5) |
Nov
(2) |
Dec
(3) |
2006 |
Jan
(8) |
Feb
(3) |
Mar
(8) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <rin...@me...> - 2001-11-13 08:28:46
|
Hello guys, I looked on the files section of the SF page and it mentions that the 0.01 build is from August 12, and the latest development build is even older. Can one of you tell me when I can expect a new official version (0.02?) ? Ringo |
From: <rin...@me...> - 2001-11-13 08:25:20
|
Hello guys, Here is a little contribution: a mock object for a PropertyChangeListener. In correspondence to the io and sql subpackages, I put this class in a beans subpackage, since the PropertyChangeListener interface resides in java.beans. ---- Start of class -------------- package com.mockobjects.beans; import java.beans.*; import com.mockobjects.*; /** * Mock object for a PropertyChangeListener. * This mock object can be used in verifying the event propagation mechanism * of beans. You can set the information you expect from a PropertyChangeEvent * as well as the number of events. If you expect more than one event, only the * actual values of the last event are stored. * * @author Ringo De Smet - <a href="http://www.mediagenix.com">MediaGeniX NV</a> */ public class MockPropertyChangeListener extends MockObject implements PropertyChangeListener { protected ExpectationValue expectedPropertyName; protected ExpectationValue expectedOldValue; protected ExpectationValue expectedNewValue; protected ExpectationCounter expectedNrOfEvents; public MockPropertyChangeListener() { this.expectedPropertyName = new ExpectationValue("MockPropertyChangeListener.propertyName"); this.expectedOldValue = new ExpectationValue("MockPropertyChangeListener.oldValue"); this.expectedNewValue = new ExpectationValue("MockPropertyChangeListener.newValue"); this.expectedNrOfEvents = new ExpectationCounter("MockPropertyChangeListener.expectedNrOfEvents"); } public MockPropertyChangeListener(String name) { this.expectedPropertyName = new ExpectationValue(name + ".propertyName"); this.expectedOldValue = new ExpectationValue(name + ".oldValue"); this.expectedNewValue = new ExpectationValue(name + ".newValue"); this.expectedNrOfEvents = new ExpectationCounter(name + ".expectedNrOfEvents"); } public void propertyChange(PropertyChangeEvent evt) { this.expectedPropertyName.setActual(evt.getPropertyName()); this.expectedOldValue.setActual(evt.getOldValue()); this.expectedNewValue.setActual(evt.getNewValue()); this.expectedNrOfEvents.inc(); } public void setExpectedNewValue(Object expectedNewValue) { this.expectedNewValue.setExpected(expectedNewValue); } public void setExpectedNrOfEvents(int nrOfExpectedEvents) { this.expectedNrOfEvents.setExpected(nrOfExpectedEvents); } public void setExpectedOldValue(Object expectedOldValue) { this.expectedOldValue.setExpected(expectedOldValue); } public void setExpectedPropertyName(String expectedPropertyName) { this.expectedPropertyName.setExpected(expectedPropertyName); } } -- End of class ---------------------- Ringo |
From: <rin...@me...> - 2001-11-13 08:06:51
|
> If I remember correctly that's what I proposed some time ago > so I am more > than happy with that :-). I would even go as far as : > > core (contains mock core library) > j2se (contains j2se mocks) > j2ee (contains j2ee mocks) > extensions (contains other "exotic" libraries) > > That we deliver as mo-java-core.jar, mo-java-j2se.jar, > mo-java-j2ee.jar and > mo-java-extensions.jar I fully agree. I sometimes have a fight here in the office with people that want to put everything in one jar without thinking about dependencies. Your proposed split-up clearly identifies this dependencies. Core - If you want to use the mock JUnit extension j2se - Mock objects if you have the standard JDK j2ee - Mock objects if you have the enterprise JDK My vote: +1 Ringo |
From: Vincent M. <vm...@oc...> - 2001-11-12 22:40:54
|
If I remember correctly that's what I proposed some time ago so I am more than happy with that :-). I would even go as far as : core (contains mock core library) j2se (contains j2se mocks) j2ee (contains j2ee mocks) extensions (contains other "exotic" libraries) That we deliver as mo-java-core.jar, mo-java-j2se.jar, mo-java-j2ee.jar and mo-java-extensions.jar -Vincent ----- Original Message ----- From: "Steve Freeman" <st...@m3...> To: <moc...@li...> Sent: Monday, November 12, 2001 10:16 PM Subject: [Mockobjects-java-dev] build organisation > with the addition of the jms library, it strikes me that we should add a new subsection to our src directory, which would give us: > core > examples > extensions > > and, possibly, > > j2ee > servlets > jms > > As the library grows, I think that would give a better balance to the build. > > Steve > > 2?¡¸rÛjözùSX,X´Ê'?yË«uëS˲þX¶Ëº·~SzwþX¶ÏSËús?¡¸rÛjö |
From: Vincent M. <vm...@oc...> - 2001-11-12 22:39:38
|
Do you mean that the mocks are not complete or that the mocked api is 2.1 ? (I'm not sure about the version but that's my guess seen that you guys were using vajava when you wrote it ... :) ). I think we should have at least 2 packages : one for servlet api 2.2 and one for servlet api 2.3. you doing it Steve ... :-) Thanks -Vincent ----- Original Message ----- From: "Steve Freeman" <st...@m3...> To: <moc...@li...> Sent: Monday, November 12, 2001 10:18 PM Subject: [Mockobjects-java-dev] servlets library > I think the current implementation of servlets is a bit out of date. Anyone object to bringing it up to date? > > I think the new stuff is largely additive, so there shouldn't be a backwards compatibility issue. > > Steve > > 2¡rjzS,´'yuS²q®z¶þ¶º~zþ¶Ss¡rj |
From: Steve F. <st...@m3...> - 2001-11-12 22:18:00
|
d2l0aCB0aGUgYWRkaXRpb24gb2YgdGhlIGptcyBsaWJyYXJ5LCBpdCBzdHJpa2VzIG1lIHRoYXQg d2Ugc2hvdWxkIGFkZCBhIG5ldyBzdWJzZWN0aW9uIHRvIG91ciBzcmMgZGlyZWN0b3J5LCB3aGlj aCB3b3VsZCBnaXZlIHVzOg0KICBjb3JlDQogIGV4YW1wbGVzDQogIGV4dGVuc2lvbnMNCg0KYW5k LCBwb3NzaWJseSwNCg0KICBqMmVlDQogICAgc2VydmxldHMNCiAgICBqbXMNCg0KQXMgdGhlIGxp YnJhcnkgZ3Jvd3MsIEkgdGhpbmsgdGhhdCB3b3VsZCBnaXZlIGEgYmV0dGVyIGJhbGFuY2UgdG8g dGhlIGJ1aWxkLg0KDQpTdGV2ZQ0KDQo= |
From: Steve F. <st...@m3...> - 2001-11-12 22:18:00
|
SSB0aGluayB0aGUgY3VycmVudCBpbXBsZW1lbnRhdGlvbiBvZiBzZXJ2bGV0cyBpcyBhIGJpdCBv dXQgb2YgZGF0ZS4gQW55b25lIG9iamVjdCB0byBicmluZ2luZyBpdCB1cCB0byBkYXRlPw0KDQpJ IHRoaW5rIHRoZSBuZXcgc3R1ZmYgaXMgbGFyZ2VseSBhZGRpdGl2ZSwgc28gdGhlcmUgc2hvdWxk bid0IGJlIGEgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkgaXNzdWUuDQoNClN0ZXZlDQoNCg== |
From: <vt...@sy...> - 2001-11-05 07:17:18
|
Thanks for the answer. You understood the question right, except for the fact that we dont use = JNDI but a simple in memory registry implementation, so its a standard = JMV call and we dont suffer the performance penalty.=20 I like what you propose. This is effectively the way for us to remove a = lot of annoying code going to that in memory registry to access a = service or factory. If I got you right, my DataAccessService classes can = be passed a DataSource so I can use mock implementations of data sources = for test. My Domain classes are passed a DAO factory so I can use mock = DataAccessService classes as well. My use case controllers (classes that = use domain objects) are passed a business facade which I can substitute = at runtime too for one that will return mock domain classes if I wish. = Up to the point where my startup component (which doesnt use any use = case controller but just start them for instance, do I dont need to test = it against mock objects) is responsible for instanciating the business = facade. Damn I like it :-) Thanks Vincent From: Vincent Massol=20 To: Vincent Tenc=E9=20 Cc: moc...@li...=20 Sent: Friday, November 02, 2001 3:46 PM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, I'm copying the list as I think they will find this question of = interest. ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Sent: Thursday, November 01, 2001 12:22 AM Subject: Re: [Mockobjects-java-dev] Interested in participating No pb. I myself have a lot of things to do at the moment. The = project is entering in its final phase and the next month will be pretty = crazy. I am afraid I wont have a lot of free time. We want to develop = lots of mock objects for our domain classes and service classes and = rewrite our tests using them.=20 use cactus, you don't need to write mocks ... No, I'm just teasing ... = ! :) A quick question. We are always facing the same issue and I would = like to know what is the approach you would recommend. The question is, = how do we substitute our domain objects for mock implementations at = runtime ? Here is what we do currently: 1) We define our domain/service objects in terms of abstract classes = or preferably interfaces 2) We add a Facade (for domain packages or service packages for = instance) or a Factory (i.e. for DAOs) to create or access instances of = objects 3) We have a kind of JNDI registry that we lookup at runtime for = components by role (like a specific service provider), roles are defined = in terms of interfaces We can then setup our tests by binding to the registry mock = implementations of specific components (roles) and our code under test = looks the registry up and gets mock objects. I suppose you have faced = this pb before. What was your approach ? What I don't like too much witht this approach is that for testing = consideration, you're going to suffer performance penalties and = implement something more complex than it should be (I may not be = understanding correctly what you said) : - each call from one class to another will have to go through JNDI, = instead of the standard intra JVM method to method call. Yes, you can = improve the performance a bit by providing a facade/factory that would = cache the objects but then you'll need to provide your own garbage = collection mechanism ... What you are doing in effect is replacing the = inter-class communication mechanism by providing a loosely coupled one. = This can be a good architecture *but* at the component level and = certainly not at the class level where you'll suffer horrible = performances ... What are the other options ? Answer : good design. A good design will = try to minimise dependencies between classes. One solution is the IOC = (inversion of control) pattern. In summary it says that instead of = having your class instanciate some domain objects, they are passed to = it. For example, let's imagine that your class to test need to connect to = a database. A first implementation could be : Implementation 1: public class MyClass { public void doSomething() { // some JDBC code } } second implementation: you factor the code using the JDBC API in a = JDBCWrapper class (or better yet, in a JDBCDataAccessService class, = implementing a DataAccessService interface). Now, your doSomething() = method does not use JDBC but rather the DataAccessService class : Implementation 2: public class MyClass { public void doSomething() { DataAccess dataService =3D new JDBCDataAccess(); // code that uses dataService } } This is better but not enough to be able to unit test MyClass. What we = need is the possibility to replace JDBCDataAccess by a mock = implementation. Instead of creating the object in doSomething(), you're = going to let the caller pass the implementation to MyClass ... The = result: your MyClass is now more generic and able to work with JDBC, = with an OODB, with flat files, ... and with mocks ... Implementation 3: public class MyClass { private DataAccessService service; public void setDataAccessService(DataAccessService service) { this.service =3D service; } public void doSomething() { // codde that uses this.service } } Note: if you want to be sure that setDataAccessService has been = called, put it as a parameter of your constructor (or of the = doSomething() method). Now, your unit test : public void testDoSomething() { // init MockDataAccessService service =3D new MockDataAccessService(); MyClass class =3D new MyClass(); class.setDataAccessService(service); // test class.doSomething(); // verify assertEquals( .....); } -Vincent PS: I hope I have understood your question ! Thanks, Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9=20 Sent: Wednesday, October 31, 2001 6:49 PM Subject: Re: [Mockobjects-java-dev] Interested in participating Vince, I just realized I have not answered to you. Sorry about = that ! can you wait 2 more days ... will do that this coming week end = (It needs to thinking ... :) ). thanks cheers, -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Cc: vin...@ge...=20 Sent: Monday, October 22, 2001 2:20 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, It's interesting to discuss the pros and cons of MockObjects vs = in-container testing. We are using Catcus a part of our test strategy = and we had some debates ourselves at the beginning on what was the best = approach to use. We quickly realised though that both approaches were = complementary. When the project began, the domain code was still rather simple = and running tests was not an issue. But as soon as we added mor elogic = (domain, crypto) and we started interfacing with external systems (DBs, = JMS providers), we notices that running tests took longer and longer. = We had to develop simulators for external systems, create tests data for = databases and back-end systems accessed through JMS as well as encrypted = incoming data that would go through our cypto libs. It was rather = frustating to see that frequently our domain code was bug-free but our = simulators or data were corrupted. We measured that 2 times out of 3 = tests failures were not due to our domain code. Worst, we had to fix the = bugs before going on and we found it 2 to 3 times longer fixing those = bugs simply because we had no tests to back up our simulators or test = data! I didnt express myself correctly saying that we encountered = limitations with our in-container testing strategy. It was rather our = test strategy as a whole that proved to be poor. Mock objects solved a = lot of our problems with its capacity to do really fine grained unit = tests. We rewrote some of the critical tests that caused problems (DBs, = JMS, Crypto) and now those tests run correclty 100% of the time. We = still use Cactus, but at a much higher level, to test our use cases. So = its like integration tests on a use case by use case basis. To focus on the discussion Mock vs In-container, in our opinion = one of the big advantage of mock objects is the capacity to write very = focused unit tests and sometimes testing pieces of code it would be = difficult to test otherwise. The tests can run very quickly and = independently (most of the time) of external settings ensuring that once = they have passed, we dont have to worry about them anymore. A side = effect of using mock objects was that we ended up writing better code, = thought more in terns of interfaces, removing unecessary dependencies = between domain objects and singletons for instance. On the other hand, using mo proved to be challenging and it = still is. It took time to understand and use them properly, then it took = time to write our own properly (respecting the concepts). We also had to = change parts of our code to introduce the mock objects, adding factories = and registry so that domain objects implementations can be replaced by = mock objects implementations during testing. But once they are written, = like tests, we can leverage mo on subsequent projects. I would imagine that If you participate in both Catcus and MO, = you also feel that both approaches are complementary :-) I will be very busy in the next weeks or so on my project, but I = can still spare some free time to help on the project. Let me think of = the best way for me to help. Regards, Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9 ; = moc...@li...=20 Sent: Thursday, October 18, 2001 4:36 AM Subject: Re: [Mockobjects-java-dev] Interested in = participating Hi Vince, ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: moc...@li...=20 Sent: Thursday, October 18, 2001 3:36 AM Subject: [Mockobjects-java-dev] Interested in participating Hi all, First of all thanks for the framework, it is of unvaluable = use. Great work.=20 thanks :) We have been developping servlets and were doing = in-container testing up to recently. The limitations that we encountered = forced us to look for a different testing=20 I would be interested in hearing these as I'm participating to = both Cactus (in-container testing) and Mock Objects. I have my own ideas = on the pros and cons of each but would be very interested to hear from = someone who has really used both approaches, on a real project. approach until we read about mock objects. We used the mock = objects servlet package, extended it for our own use, measured the = benefits then decided to develop a mock objects JMS package (it works = fine and serves us well). Now we plan to convert all our DAO unit tests = to using mock objects. Like with JUnit tests, since we tried mock objects, we have = a hard time doing our tests any different way. So if you guys would = accept some help we would be pleased to participate in the effort, being = addicted mock objects users ourselves! that's really great to hear ! :o) Welcome aboard. I fear we = haven't had as much time as we would like so far and any help is = welcome. Have a look at CVS and choose what you'd like to do. = Unfortunately we've not made a todo list yet. Maybe you could propose = items for the todo list and volunteer for some of them ? These are the = different areas where you could help : - improve existing code and especially code that is not = finished. The servlet mocks for example are far from being finished. - improve build process - improve web site documentation - help answer to mailing-lists posts - promote mock objects around you (I'm sure you're already = doing that !) - anything else you might think of Regards, Vincent Tenc=E9 -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting |
From: Steve F. <sm...@us...> - 2001-11-05 02:49:30
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv32173 Modified Files: papers.xml Log Message: added how_mocks_happened Index: papers.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers.xml,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- papers.xml 2001/09/20 21:32:57 1.3 +++ papers.xml 2001/11/05 02:49:27 1.4 @@ -23,6 +23,15 @@ Mock Objects can lead to more rigourous unit testing and, I believe, better structured code than working with a real database. <em>Steve Freeman</em> </p> </li> + <li> + <link href="papers/how_mocks_happened.html">How Mock Objects happened</link> + <p> + This article uses a simple example to show a compressed history of how Mock Objects + was discovered by refactoring from conventional unit tests and what its advantages are. + <em>Steve Freeman</em> </p> + </li> + + </sl> </s1> |
From: Steve F. <sm...@us...> - 2001-11-05 02:48:56
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv32038 Added Files: how_mocks_happened.html Log Message: renamed from brief_introduction --- NEW FILE: how_mocks_happened.html --- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>How Mock Objects happened</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <style type="text/css"> <!-- .deemphasised { color: #666666} h1 { text-align: center; font-family: Arial, Helvetica, sans-serif; font-weight: bold} h3 { font-family: Arial, Helvetica, sans-serif; font-style: italic; font-weight: bold; font-size: small} .inline_code { font-family: "Courier New", Courier, mono; font-style: normal; vertical-align: middle} p { font-family: Arial, Helvetica, sans-serif; margin-left: 5%} li { font-family: Arial, Helvetica, sans-serif } h2 { font-family: Arial, Helvetica, sans-serif; margin-top: 3%} pre { margin-left: 7%} --> </style> </head> <body bgcolor="#ffffff"> <h1>How Mock Objects happened</h1> <p align="Center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> <p>Mock Objects is a development technique that lets you unit test classes that you didn't think you could <em>and</em> helps you write better code whilst doing so. This article uses a simple example to show a compressed history of how Mock Objects was discovered by refactoring from conventional unit tests and what its advantages are.</p> <p>Mock Objects is based on two key concepts: replace everything except the code under test with mock implementations that emulate the rest of the environement, and put the test assertions <em>inside</em> those mock implementations so that you can validate the interactions between the objects in your test case. </p> <p>This article assumes that you are familiar with java and unit testing using JUnit.</p> <h2>Simple unit tests</h2> <p>Let's begin with an example. Most people start writing unit tests by creating an object, calling some methods on it, and then checking its state. For example, imagine that I'm writing a component automatically to direct robots through a warehouse. Given a start point on a grid, it will find a route through the stacks to a given destination. The tests will have to tell me that the robot arrives correctly and does not hit anything on the way. A first test might be: </p> <pre>public class TestRobot { [...] public void setUp() { robot = new Robot(); } public void testGotoSamePlace() { final Position POSITION = new Position(1, 1); robot.setCurrentPosition(POSITION); robot.goTo(POSITION); assertEquals("Should be same", POSITION, robot.getPosition()); } }</pre> <p>This tells me that the robot thinks that it has arrived, but it doesn't tell me anything about how it got there. I would like to know that the Robot didn't move in this test. One option would be to store and retrieve the route it took after each <em>goto()</em>. For example:</p> <pre>public void testGotoSamePlace() { final Position POSITION = new Position(0, 0); robot.setCurrentPosition(POSITION); robot.goTo(POSITION); assertEquals("Should be empty", 0, robot.getRecentMoveRequests().size()); assertEquals("Should be same", POSITION, robot.getPosition()); }</pre> <p>My next test might be to move one point on the grid:</p> <pre>public void testMoveOnePoint() { final Position DESTINATION = new Position(1, 0); robot.setCurrentPosition(new Position(0, 0)); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); List moves = robot.getRecentMoveRequests(); assertEquals("Should be one move", 1, moves.size()); assertEquals("Should be same move", new MoveRequest(1, MoveRequest.SOUTH), moves.get(1)); }</pre> <p>As the tests become more complex, I would pull out some of the detail into helper methods to make the code more legible:</p> <pre>public void testMoveALongWay() { final Position DESTINATION = new Position(34, 71); robot.setCurrentPosition(new Position(0, 0)); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); assertEquals("Should be same moves", makeExpectedLongWayMoves(), robot.getRecentMoves()); }</pre> <p>Where <em>makeExpectedLongWayMoves()</em> returns another list of the moves I expect the robot to take for this test. </p> <p>There are problems with this approach to testing. First, tests like this are effectively small-scale integration tests, they set up pre-conditions and test post-conditions but they have no access to the code while it is running. If one of these tests fail, I will have to step through the code to find the problem because the assertions have been made <em>after</em> the call has finished. Second, to test this behaviour at all, I had to add some functionality to the production class, namely retaining all the <em>MoveRequest</em>s since the last <em>goto()</em>. I don't have any other immediate need for <em>getRecentMovesRequests()</em>. Third, although it's not the case here, test suites based on extracting history from objects tend to need lots of utilities for constructing and comparing collections of values. The need to write external code to manipulate an object is often a warning that its class is incomplete and that some behaviour should be moved from the utility to the class.</p> <p>Is there a better way? Can I find a style that will give me better error reporting and put the behaviour in the right place? </p> <h2>Factoring out the motor</h2> <p>One thing I can be sure of is that the Robot contains a <em>Motor</em> that responds to these move requests. If I can intercept those requests, I can track what's happening in the Robot. A first step would be to define an interface to the Motor:</p> <pre>public interface Motor { void move(MoveRequest request); }</pre> <p>I can pass an instance of Motor to the Robot, perhaps via its constructor, and a simple implementation might be:</p> <pre>public class OneSpeedMotor implements Motor { public void move(MoveRequest request) { turnBy(request.getTurn()); advance(request.getDistance()); } [...] }</pre> <p>I can now refactor my tests and replace the real Motor with a MockMotor that can watch what's happening in the Robot, and complain as soon as something goes wrong. The first test is now:</p> <pre>public void testGotoSamePlace() { final Position POSITION = new Position(0, 0); robot.setCurrentPosition(POSITION); Motor mockMotor = new Motor() { public void move(MoveRequest request) { fail("There should be no moves in this test"); } }; robot.setMotor(mockMotor); robot.goTo(POSITION); assertEquals("Should be same", POSITION, robot.getPosition()); }</pre> <p>In this test, if there is a bug in the Robot code and the Motor gets requested to move, the mock implementation of <em>move()</em> will fail immediately and stop the test; I no longer need to ask the Robot where it's been. As my tests grow, I can refactor the various mock implementations into a single, more sophisticated MockMotor and use it throughout all the Robot tests; for example:</p> <pre>public void MockMotor implements Motor { private ArrayList expectedRequests = new ArrayList(); public void move(MoveRequest request) { assert("Too many requests", this.expectedRequests.size() > 0); assertEquals("Should be next request", this.expectedRequests.remove(0), request); } public void addExpectedRequest(MoveRequest request) { this.expectedRequests.add(request); } public void verify() { assertEquals("Too few requests", 0, this.expectedRequests.size()); } }</pre> <p>Which makes our tests look like:</p> <pre>public class TestRobot { [...] static final Position ORIGIN = new Position(0, 0); public void setUp() { mockMotor = new MockMotor(); robot = new Robot(mockMotor); robot.setCurrentPosition(ORIGIN); } public void testGotoSamePlace() { robot.goTo(ORIGIN); assertEquals("Should be same", ORIGIN, robot.getPosition()); mockMotor.verify(); } public void testMoveOnePoint() { final Position DESTINATION = new Position(1, 0); mockRobot.addExpectedRequest(new MoveRequest(1, MoveRequest.SOUTH)); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); mockMotor.verify(); } public void testMoveALongWay() { final Position DESTINATION = new Position(34, 71); mockMotor.addExpectedRequests(makeExpectedLongWayMoveRequests()); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); mockMotor.verify(); } }</pre> <h2>What does this mean?</h2> <p>My code moved in this direction because I was committed to unit testing but didn't want to expose unnecessary details about the state of my Robot (the <em>getRecentMoveRequests()</em> method). As a result, I find that I have better unit tests and that I have clarified some of the internal structure of the Robot by adding a <em>Motor</em> interface. I now have the flexibility to subtitute a different Motor implementation, perhaps one that accelerates. Similarly, if I want to track the total distance a Robot travels, I can do so without changing the implementation of either the Robot or Motor:</p> <pre> /** * A decorator that accumulates distances, then passes the request * on to a real Motor. */ public class MotorTracker implements Motor { private Motor realMotor; private long totalDistance = 0; public MotorTracker(Motor aRealMotor) { realMotor = aRealMotor; } public void move(MoveRequest request) { totalDistance += request.getDistance(); realMotor.move(request); } public long getTotalDistance() { return totalDistance; } } // When constructing the Robot, wrap the implementation of a // Motor that does the work in a MotorTracker. OneSpeedMotor realMotor = new OneSpeedMotor(); MotorTracker motorTracker = new MotorTracker(realMotor); Robot = new Robot(motorTracker); // do some travelling here [...] println("Total distance was: " + motorTracker.getTotalDistance()); </pre> <p>Neither changing the Motor implementation nor adding tracking functionality would have been so easy if I had stuck with a testing strategy based on stashing the intermediate route. The next step might be to introduce a <em>Navigator</em> object to work out the route, and the Robot would then link the two together.</p> <p>Tests based on Mock Objects usually conform to a pattern: setup any state, set expectations for the test, run the target code, and verify that your expectations have been met. This style makes tests easy to work with because they look similar and because all the interactions with an object are local to a test fixture; I have found myself contributing usefully to someone else's code base after only a few minutes with the tests. More importantly, however, I constantly find that the process of deciding what to verify in a test drives me to clarify the relationships between an object and its collaborators. The flex points I add to my code to provide support for testing turn out to be the flex points I need as the use of the code evolves. </p> <h2>Conclusions</h2> <p>This simple example shows how refactoring tests with some design principles in mind led to the discovery of an unusually fruitful development technique. Using Mock Objects in practice is slightly different. First, the process of writing a test usually involves defining which mock objects are involved, rather than extracting them afterwards. Second, in Java at least, there are the beginnings of a Mock Object library for common types. Third, there are now several tools and libraries to help with the construction of Mock Objects. In particular, the <a href="http://www.mockobjects.com">www.mockobjects.com</a> site includes a library of expectation objects. </p> <hr> <p>© Steve Freeman 2001</p> </body> </html> |
From: Steve F. <st...@m3...> - 2001-11-05 02:42:43
|
dGhlcmUgaGF2ZSBiZWVuIHNvbWUgY2hhbmdlcyBpbiB0aGUgbGlicmFyeSwgaW4gcGFydGljdWxh ciB0aGUgaW50cm9kdWN0aW9uIG9mIHRoZSBqbXMgcGFja2FnZSBhbmQgSSd2ZSBiZWVuIHdvcmtp bmcgb24gc29tZSBwYXBlcnMsIHNvIG1heWJlIGl0J3MgdGltZSB0byBidWlsZCBhIG5ldyB2ZXJz aW9uPw0KDQpTdGV2ZQ0KDQo= |
From: Steve F. <sm...@us...> - 2001-11-05 02:38:44
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv29591 Modified Files: brief_introduction.html Log Message: more contents Index: brief_introduction.html =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers/brief_introduction.html,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- brief_introduction.html 2001/11/03 00:33:31 1.3 +++ brief_introduction.html 2001/11/05 02:38:38 1.4 @@ -1,10 +1,8 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> - <title>Developing JDBC applications test-first</title> - + <title>How Mock Objects happened</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <style type="text/css"> <!-- .deemphasised { color: #666666} @@ -19,34 +17,36 @@ </style> </head> <body bgcolor="#ffffff"> -<h1>How Mock Objects happen</h1> +<h1>How Mock Objects happened</h1> <p align="Center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> -<p>Unit testing is a fundamental practice in Extreme Programming, but -most nontrivial code is difficult to test in isolation. You need to -make sure that you test one feature at a time, and you want to be -notified as soon as any problem occurs. Normal unit testing is hard -because you are trying to test the code from outside. </p> - -<p>Mock Objects is an approach to writing unit tests based on two key -techniques: replace everything except the code under test with mock -implementations that emulate the rest of the environement, and put the -test assertions <em>inside</em> those mock implementations so that you -can validate the interactions between the objects in your test -case.</p> +<p>Mock Objects is a development technique that lets you unit test +classes that you didn't think you could <em>and</em> helps you write +better code whilst doing so. This article uses a simple example to +show a compressed history of how Mock Objects was discovered by +refactoring from conventional unit tests and what its advantages +are.</p> + +<p>Mock Objects is based on two key concepts: replace everything +except the code under test with mock implementations that emulate the +rest of the environement, and put the test assertions <em>inside</em> +those mock implementations so that you can validate the interactions +between the objects in your test case. </p> -<p>This article assumes that you are familiar with java and unit testing using JUnit</p> +<p>This article assumes that you are familiar with java and unit +testing using JUnit.</p> <h2>Simple unit tests</h2> -<p>Most people start writing unit tests by creating an object, calling -some methods on it, and then checking its state. For example, imagine -that I'm writing a component automatically to direct robots through a -warehouse. Given a start point on a grid, it will find a route through -the stacks to a given destination. The tests will have to tell me that -the robot arrives correctly and does not hit anything on the way. A -first test might be: </p> +<p>Let's begin with an example. Most people start writing unit +tests by creating an object, calling some methods on it, and then +checking its state. For example, imagine that I'm writing a component +automatically to direct robots through a warehouse. Given a start +point on a grid, it will find a route through the stacks to a given +destination. The tests will have to tell me that the robot arrives +correctly and does not hit anything on the way. A first test might be: +</p> <pre>public class TestRobot { [...] @@ -114,7 +114,7 @@ step through the code to find the problem because the assertions have been made <em>after</em> the call has finished. Second, to test this behaviour at all, I had to add some functionality to the production -class, namely retaining all the MoveRequests since the last +class, namely retaining all the <em>MoveRequest</em>s since the last <em>goto()</em>. I don't have any other immediate need for <em>getRecentMovesRequests()</em>. Third, although it's not the case here, test suites based on extracting history from objects tend to need lots @@ -128,10 +128,10 @@ <h2>Factoring out the motor</h2> -<p>One thing I can be sure of is that the Robot contains a Motor that -responds to these move requests. If I can intercept those requests, I -can track what's happening in the Robot. A first step would be to -define an interface to the Motor:</p> +<p>One thing I can be sure of is that the Robot contains a +<em>Motor</em> that responds to these move requests. If I can +intercept those requests, I can track what's happening in the Robot. A +first step would be to define an interface to the Motor:</p> <pre>public interface Motor { void move(MoveRequest request); @@ -225,24 +225,86 @@ assertEquals("Should be destination", DESTINATION, robot.getPosition()); mockMotor.verify(); + } }</pre> + +<h2>What does this mean?</h2> + +<p>My code moved in this direction because I was committed to unit +testing but didn't want to expose unnecessary details about the state +of my Robot (the <em>getRecentMoveRequests()</em> method). As a +result, I find that I have better unit tests and that I have clarified +some of the internal structure of the Robot by adding a +<em>Motor</em> interface. I now have the flexibility to subtitute a +different Motor implementation, perhaps one that +accelerates. Similarly, if I want to track the total distance a Robot +travels, I can do so without changing the implementation of either the +Robot or Motor:</p> + +<pre> +/** + * A decorator that accumulates distances, then passes the request + * on to a real Motor. + */ +public class MotorTracker implements Motor { + private Motor realMotor; + private long totalDistance = 0; + + public MotorTracker(Motor aRealMotor) { + realMotor = aRealMotor; + } + public void move(MoveRequest request) { + totalDistance += request.getDistance(); + realMotor.move(request); + } + public long getTotalDistance() { + return totalDistance; + } +} + +// When constructing the Robot, wrap the implementation of a +// Motor that does the work in a MotorTracker. +OneSpeedMotor realMotor = new OneSpeedMotor(); +MotorTracker motorTracker = new MotorTracker(realMotor); +Robot = new Robot(motorTracker); +// do some travelling here [...] +println("Total distance was: " + motorTracker.getTotalDistance()); +</pre> + +<p>Neither changing the Motor implementation nor adding tracking +functionality would have been so easy if I had stuck with a testing +strategy based on stashing the intermediate route. The next step might +be to introduce a <em>Navigator</em> object to work out the route, +and the Robot would then link the two together.</p> + +<p>Tests based on Mock Objects usually conform to a pattern: setup any +state, set expectations for the test, run the target code, and verify +that your expectations have been met. This style makes tests easy to +work with because they look similar and because all the interactions +with an object are local to a test fixture; I have found myself +contributing usefully to someone else's code base after only a few +minutes with the tests. More importantly, however, I constantly find +that the process of deciding what to verify in a test drives me to +clarify the relationships between an object and its collaborators. +The flex points I add to my code to provide support for testing turn +out to be the flex points I need as the use of the code evolves. +</p> + +<h2>Conclusions</h2> + +<p>This simple example shows how refactoring tests with some design +principles in mind led to the discovery of an unusually fruitful +development technique. Using Mock Objects in practice is slightly +different. First, the process of writing a test usually involves +defining which mock objects are involved, rather than extracting them +afterwards. Second, in Java at least, there are the beginnings of a +Mock Object library for common types. Third, there are now several +tools and libraries to help with the construction of Mock Objects. In +particular, the <a +href="http://www.mockobjects.com">www.mockobjects.com</a> site +includes a library of expectation objects. +</p> -<h2>What has this change achieved?</h2> -<ul> - <li><em>Refactored test infrastructure</em> As my tests grow, I find myself repeating my test - infrastructure. I can push those repetitions into mock implementations, based on the - expectation types that ship with the Mock Objects library. </li> - <li><em>Failing early</em> A key experience of developing test-first is learning to make test - failures self explanatory. Failing at the time an error occurs with a useful message, - rather than at the end of the test, is a powerful tool.</li> - <li><em>Driving design</em> Choosing which objects to verify in a test turns out to be an - effective tool for deriving the structure of a class and its neighbours.</li> - <li><em>Consistent test structure</em> Most unit tests in this style have a similar structure: - set up the objects (pre-conditions), set the expected behaviour (invariants and - post-conditions), exercise the object, and then verify whether the expectations were - fufilled. This style makes tests easy to work with because they look similar and because - all the interactions with an object are local to a test fixture.</li> -</ul> <hr> <p>© Steve Freeman 2001</p> </body> |
From: Steve F. <sm...@us...> - 2001-11-05 00:12:15
|
Update of /cvsroot/mockobjects/mockobjects-java In directory usw-pr-cvs1:/tmp/cvs-serv25555 Modified Files: build.properties.sample Log Message: Added a commented out property lib.dir to the build.properties.sample to show that the entire lib directory could be changed with a small setting in your local build.properties. Index: build.properties.sample =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/build.properties.sample,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- build.properties.sample 2001/07/31 15:52:29 1.3 +++ build.properties.sample 2001/11/05 00:12:13 1.4 @@ -5,3 +5,7 @@ # Distribution directory (if none is specified here, it will default to # 'dist' #dist.dir = e:/tmp/mockobjects-dist + +# You can also change your libraries directory. If none is specified here, it +# will default to 'lib' +#lib.dir = lib |
From: Steve F. <sm...@us...> - 2001-11-05 00:11:53
|
Update of /cvsroot/mockobjects/mockobjects-java In directory usw-pr-cvs1:/tmp/cvs-serv25498 Modified Files: build.xml Log Message: Updated the build.xml to skip compiling the jms mocks if the javax.jms.Message class cannot be found in the classpath, much like how the servlet stuff is skipped. Changed the order of build.properties processing to read the user's build.properties before the project's build.properties. This way you can define locations and use them in the specific props for projects. Index: build.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/build.xml,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- build.xml 2001/09/09 00:49:57 1.14 +++ build.xml 2001/11/05 00:11:51 1.15 @@ -33,8 +33,8 @@ <!-- Give user a chance to override without editing this file (and without typing -D each time it compiles it) --> - <property file="build.properties" /> <property file="${user.home}/build.properties" /> + <property file="build.properties" /> <!-- Generic project properties --> <property name="project.fullname" value="Mock Objects"/> @@ -164,6 +164,10 @@ classname="javax.servlet.Servlet" classpathref="lib.classpath"/> + <available property="jms.present" + classname="javax.jms.Message" + classpathref="lib.classpath"/> + </target> <!-- @@ -251,6 +255,7 @@ <!-- Exclude all java files for which no library is present --> <exclude name="${package.prefix}/servlet/**" unless="servlet.present"/> + <exclude name="${package.prefix}/jms/**" unless="jms.present"/> <classpath> <path refid="lib.classpath"/> |
From: Vincent M. <vm...@oc...> - 2001-11-04 23:21:22
|
----- Original Message -----=20 From: Tim Mackinnon=20 To: Vincent Massol ; Vincent Tenc=E9=20 Cc: moc...@li...=20 Sent: Sunday, November 04, 2001 11:11 PM Subject: RE: [Mockobjects-java-dev] Interested in participating Nice answer Vincent - the only modification I would make would be that = instead of having: class.setDataAccessService(service); I would put the ability to set a service in the constructor (unless = you really have a need to dynamically switch services at runtime - as I = think this is clearer). You can then provide a default constructor that = does something like: this(new Service()) thanks Tim ... :) I agree completely with you and actually even = suggested that in my response : "Note: if you want to be sure that = setDataAccessService has been called, put it as a parameter of your = constructor (or of the doSomething() method)". Of course, you're = explaining it better here ... :-) cheers, -Vincent Tim -----Original Message----- From: moc...@li... = [mailto:moc...@li...]On Behalf Of = Vincent Massol Sent: 2 November 2001 8:46 pm To: Vincent Tenc=E9 Cc: moc...@li... Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, I'm copying the list as I think they will find this question of = interest. ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Sent: Thursday, November 01, 2001 12:22 AM Subject: Re: [Mockobjects-java-dev] Interested in participating No pb. I myself have a lot of things to do at the moment. The = project is entering in its final phase and the next month will be pretty = crazy. I am afraid I wont have a lot of free time. We want to develop = lots of mock objects for our domain classes and service classes and = rewrite our tests using them.=20 use cactus, you don't need to write mocks ... No, I'm just teasing = ... ! :) A quick question. We are always facing the same issue and I would = like to know what is the approach you would recommend. The question is, = how do we substitute our domain objects for mock implementations at = runtime ? Here is what we do currently: 1) We define our domain/service objects in terms of abstract = classes or preferably interfaces 2) We add a Facade (for domain packages or service packages for = instance) or a Factory (i.e. for DAOs) to create or access instances of = objects 3) We have a kind of JNDI registry that we lookup at runtime for = components by role (like a specific service provider), roles are defined = in terms of interfaces We can then setup our tests by binding to the registry mock = implementations of specific components (roles) and our code under test = looks the registry up and gets mock objects. I suppose you have faced = this pb before. What was your approach ? What I don't like too much witht this approach is that for testing = consideration, you're going to suffer performance penalties and = implement something more complex than it should be (I may not be = understanding correctly what you said) : - each call from one class to another will have to go through JNDI, = instead of the standard intra JVM method to method call. Yes, you can = improve the performance a bit by providing a facade/factory that would = cache the objects but then you'll need to provide your own garbage = collection mechanism ... What you are doing in effect is replacing the = inter-class communication mechanism by providing a loosely coupled one. = This can be a good architecture *but* at the component level and = certainly not at the class level where you'll suffer horrible = performances ... What are the other options ? Answer : good design. A good design = will try to minimise dependencies between classes. One solution is the = IOC (inversion of control) pattern. In summary it says that instead of = having your class instanciate some domain objects, they are passed to = it. For example, let's imagine that your class to test need to connect = to a database. A first implementation could be : Implementation 1: public class MyClass { public void doSomething() { // some JDBC code } } second implementation: you factor the code using the JDBC API in a = JDBCWrapper class (or better yet, in a JDBCDataAccessService class, = implementing a DataAccessService interface). Now, your doSomething() = method does not use JDBC but rather the DataAccessService class : Implementation 2: public class MyClass { public void doSomething() { DataAccess dataService =3D new JDBCDataAccess(); // code that uses dataService } } This is better but not enough to be able to unit test MyClass. What = we need is the possibility to replace JDBCDataAccess by a mock = implementation. Instead of creating the object in doSomething(), you're = going to let the caller pass the implementation to MyClass ... The = result: your MyClass is now more generic and able to work with JDBC, = with an OODB, with flat files, ... and with mocks ... Implementation 3: public class MyClass { private DataAccessService service; public void setDataAccessService(DataAccessService service) { this.service =3D service; } public void doSomething() { // codde that uses this.service } } Note: if you want to be sure that setDataAccessService has been = called, put it as a parameter of your constructor (or of the = doSomething() method). Now, your unit test : public void testDoSomething() { // init MockDataAccessService service =3D new MockDataAccessService(); MyClass class =3D new MyClass(); class.setDataAccessService(service); // test class.doSomething(); // verify assertEquals( .....); } -Vincent PS: I hope I have understood your question ! Thanks, Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9=20 Sent: Wednesday, October 31, 2001 6:49 PM Subject: Re: [Mockobjects-java-dev] Interested in participating Vince, I just realized I have not answered to you. Sorry about = that ! can you wait 2 more days ... will do that this coming week end = (It needs to thinking ... :) ). thanks cheers, -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Cc: vin...@ge...=20 Sent: Monday, October 22, 2001 2:20 AM Subject: Re: [Mockobjects-java-dev] Interested in = participating Hi Vince, It's interesting to discuss the pros and cons of MockObjects = vs in-container testing. We are using Catcus a part of our test strategy = and we had some debates ourselves at the beginning on what was the best = approach to use. We quickly realised though that both approaches were = complementary. When the project began, the domain code was still rather = simple and running tests was not an issue. But as soon as we added mor = elogic (domain, crypto) and we started interfacing with external systems = (DBs, JMS providers), we notices that running tests took longer and = longer. We had to develop simulators for external systems, create tests = data for databases and back-end systems accessed through JMS as well as = encrypted incoming data that would go through our cypto libs. It was = rather frustating to see that frequently our domain code was bug-free = but our simulators or data were corrupted. We measured that 2 times out = of 3 tests failures were not due to our domain code. Worst, we had to = fix the bugs before going on and we found it 2 to 3 times longer fixing = those bugs simply because we had no tests to back up our simulators or = test data! I didnt express myself correctly saying that we encountered = limitations with our in-container testing strategy. It was rather our = test strategy as a whole that proved to be poor. Mock objects solved a = lot of our problems with its capacity to do really fine grained unit = tests. We rewrote some of the critical tests that caused problems (DBs, = JMS, Crypto) and now those tests run correclty 100% of the time. We = still use Cactus, but at a much higher level, to test our use cases. So = its like integration tests on a use case by use case basis. To focus on the discussion Mock vs In-container, in our = opinion one of the big advantage of mock objects is the capacity to = write very focused unit tests and sometimes testing pieces of code it = would be difficult to test otherwise. The tests can run very quickly and = independently (most of the time) of external settings ensuring that once = they have passed, we dont have to worry about them anymore. A side = effect of using mock objects was that we ended up writing better code, = thought more in terns of interfaces, removing unecessary dependencies = between domain objects and singletons for instance. On the other hand, using mo proved to be challenging and it = still is. It took time to understand and use them properly, then it took = time to write our own properly (respecting the concepts). We also had to = change parts of our code to introduce the mock objects, adding factories = and registry so that domain objects implementations can be replaced by = mock objects implementations during testing. But once they are written, = like tests, we can leverage mo on subsequent projects. I would imagine that If you participate in both Catcus and MO, = you also feel that both approaches are complementary :-) I will be very busy in the next weeks or so on my project, but = I can still spare some free time to help on the project. Let me think of = the best way for me to help. Regards, Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9 ; = moc...@li...=20 Sent: Thursday, October 18, 2001 4:36 AM Subject: Re: [Mockobjects-java-dev] Interested in = participating Hi Vince, ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: moc...@li...=20 Sent: Thursday, October 18, 2001 3:36 AM Subject: [Mockobjects-java-dev] Interested in = participating Hi all, First of all thanks for the framework, it is of unvaluable = use. Great work.=20 thanks :) We have been developping servlets and were doing = in-container testing up to recently. The limitations that we encountered = forced us to look for a different testing=20 I would be interested in hearing these as I'm participating = to both Cactus (in-container testing) and Mock Objects. I have my own = ideas on the pros and cons of each but would be very interested to hear = from someone who has really used both approaches, on a real project. approach until we read about mock objects. We used the = mock objects servlet package, extended it for our own use, measured the = benefits then decided to develop a mock objects JMS package (it works = fine and serves us well). Now we plan to convert all our DAO unit tests = to using mock objects. Like with JUnit tests, since we tried mock objects, we = have a hard time doing our tests any different way. So if you guys would = accept some help we would be pleased to participate in the effort, being = addicted mock objects users ourselves! that's really great to hear ! :o) Welcome aboard. I fear we = haven't had as much time as we would like so far and any help is = welcome. Have a look at CVS and choose what you'd like to do. = Unfortunately we've not made a todo list yet. Maybe you could propose = items for the todo list and volunteer for some of them ? These are the = different areas where you could help : - improve existing code and especially code that is not = finished. The servlet mocks for example are far from being finished. - improve build process - improve web site documentation - help answer to mailing-lists posts - promote mock objects around you (I'm sure you're already = doing that !) - anything else you might think of Regards, Vincent Tenc=E9 -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting |
From: Tim M. <tim...@po...> - 2001-11-04 23:10:49
|
Nice answer Vincent - the only modification I would make would be that instead of having: class.setDataAccessService(service); I would put the ability to set a service in the constructor (unless you really have a need to dynamically switch services at runtime - as I think this is clearer). You can then provide a default constructor that does something like: this(new Service()) Tim -----Original Message----- From: moc...@li... [mailto:moc...@li...]On Behalf Of Vincent Massol Sent: 2 November 2001 8:46 pm To: Vincent Tencé Cc: moc...@li... Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, I'm copying the list as I think they will find this question of interest. ----- Original Message ----- From: Vincent Tencé To: Vincent Massol Sent: Thursday, November 01, 2001 12:22 AM Subject: Re: [Mockobjects-java-dev] Interested in participating No pb. I myself have a lot of things to do at the moment. The project is entering in its final phase and the next month will be pretty crazy. I am afraid I wont have a lot of free time. We want to develop lots of mock objects for our domain classes and service classes and rewrite our tests using them. use cactus, you don't need to write mocks ... No, I'm just teasing ... ! :) A quick question. We are always facing the same issue and I would like to know what is the approach you would recommend. The question is, how do we substitute our domain objects for mock implementations at runtime ? Here is what we do currently: 1) We define our domain/service objects in terms of abstract classes or preferably interfaces 2) We add a Facade (for domain packages or service packages for instance) or a Factory (i.e. for DAOs) to create or access instances of objects 3) We have a kind of JNDI registry that we lookup at runtime for components by role (like a specific service provider), roles are defined in terms of interfaces We can then setup our tests by binding to the registry mock implementations of specific components (roles) and our code under test looks the registry up and gets mock objects. I suppose you have faced this pb before. What was your approach ? What I don't like too much witht this approach is that for testing consideration, you're going to suffer performance penalties and implement something more complex than it should be (I may not be understanding correctly what you said) : - each call from one class to another will have to go through JNDI, instead of the standard intra JVM method to method call. Yes, you can improve the performance a bit by providing a facade/factory that would cache the objects but then you'll need to provide your own garbage collection mechanism ... What you are doing in effect is replacing the inter-class communication mechanism by providing a loosely coupled one. This can be a good architecture *but* at the component level and certainly not at the class level where you'll suffer horrible performances ... What are the other options ? Answer : good design. A good design will try to minimise dependencies between classes. One solution is the IOC (inversion of control) pattern. In summary it says that instead of having your class instanciate some domain objects, they are passed to it. For example, let's imagine that your class to test need to connect to a database. A first implementation could be : Implementation 1: public class MyClass { public void doSomething() { // some JDBC code } } second implementation: you factor the code using the JDBC API in a JDBCWrapper class (or better yet, in a JDBCDataAccessService class, implementing a DataAccessService interface). Now, your doSomething() method does not use JDBC but rather the DataAccessService class : Implementation 2: public class MyClass { public void doSomething() { DataAccess dataService = new JDBCDataAccess(); // code that uses dataService } } This is better but not enough to be able to unit test MyClass. What we need is the possibility to replace JDBCDataAccess by a mock implementation. Instead of creating the object in doSomething(), you're going to let the caller pass the implementation to MyClass ... The result: your MyClass is now more generic and able to work with JDBC, with an OODB, with flat files, ... and with mocks ... Implementation 3: public class MyClass { private DataAccessService service; public void setDataAccessService(DataAccessService service) { this.service = service; } public void doSomething() { // codde that uses this.service } } Note: if you want to be sure that setDataAccessService has been called, put it as a parameter of your constructor (or of the doSomething() method). Now, your unit test : public void testDoSomething() { // init MockDataAccessService service = new MockDataAccessService(); MyClass class = new MyClass(); class.setDataAccessService(service); // test class.doSomething(); // verify assertEquals( .....); } -Vincent PS: I hope I have understood your question ! Thanks, Vincent ----- Original Message ----- From: Vincent Massol To: Vincent Tencé Sent: Wednesday, October 31, 2001 6:49 PM Subject: Re: [Mockobjects-java-dev] Interested in participating Vince, I just realized I have not answered to you. Sorry about that ! can you wait 2 more days ... will do that this coming week end (It needs to thinking ... :) ). thanks cheers, -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting ----- Original Message ----- From: Vincent Tencé To: Vincent Massol Cc: vin...@ge... Sent: Monday, October 22, 2001 2:20 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, It's interesting to discuss the pros and cons of MockObjects vs in-container testing. We are using Catcus a part of our test strategy and we had some debates ourselves at the beginning on what was the best approach to use. We quickly realised though that both approaches were complementary. When the project began, the domain code was still rather simple and running tests was not an issue. But as soon as we added mor elogic (domain, crypto) and we started interfacing with external systems (DBs, JMS providers), we notices that running tests took longer and longer. We had to develop simulators for external systems, create tests data for databases and back-end systems accessed through JMS as well as encrypted incoming data that would go through our cypto libs. It was rather frustating to see that frequently our domain code was bug-free but our simulators or data were corrupted. We measured that 2 times out of 3 tests failures were not due to our domain code. Worst, we had to fix the bugs before going on and we found it 2 to 3 times longer fixing those bugs simply because we had no tests to back up our simulators or test data! I didnt express myself correctly saying that we encountered limitations with our in-container testing strategy. It was rather our test strategy as a whole that proved to be poor. Mock objects solved a lot of our problems with its capacity to do really fine grained unit tests. We rewrote some of the critical tests that caused problems (DBs, JMS, Crypto) and now those tests run correclty 100% of the time. We still use Cactus, but at a much higher level, to test our use cases. So its like integration tests on a use case by use case basis. To focus on the discussion Mock vs In-container, in our opinion one of the big advantage of mock objects is the capacity to write very focused unit tests and sometimes testing pieces of code it would be difficult to test otherwise. The tests can run very quickly and independently (most of the time) of external settings ensuring that once they have passed, we dont have to worry about them anymore. A side effect of using mock objects was that we ended up writing better code, thought more in terns of interfaces, removing unecessary dependencies between domain objects and singletons for instance. On the other hand, using mo proved to be challenging and it still is. It took time to understand and use them properly, then it took time to write our own properly (respecting the concepts). We also had to change parts of our code to introduce the mock objects, adding factories and registry so that domain objects implementations can be replaced by mock objects implementations during testing. But once they are written, like tests, we can leverage mo on subsequent projects. I would imagine that If you participate in both Catcus and MO, you also feel that both approaches are complementary :-) I will be very busy in the next weeks or so on my project, but I can still spare some free time to help on the project. Let me think of the best way for me to help. Regards, Vincent ----- Original Message ----- From: Vincent Massol To: Vincent Tencé ; moc...@li... Sent: Thursday, October 18, 2001 4:36 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, ----- Original Message ----- From: Vincent Tencé To: moc...@li... Sent: Thursday, October 18, 2001 3:36 AM Subject: [Mockobjects-java-dev] Interested in participating Hi all, First of all thanks for the framework, it is of unvaluable use. Great work. thanks :) We have been developping servlets and were doing in-container testing up to recently. The limitations that we encountered forced us to look for a different testing I would be interested in hearing these as I'm participating to both Cactus (in-container testing) and Mock Objects. I have my own ideas on the pros and cons of each but would be very interested to hear from someone who has really used both approaches, on a real project. approach until we read about mock objects. We used the mock objects servlet package, extended it for our own use, measured the benefits then decided to develop a mock objects JMS package (it works fine and serves us well). Now we plan to convert all our DAO unit tests to using mock objects. Like with JUnit tests, since we tried mock objects, we have a hard time doing our tests any different way. So if you guys would accept some help we would be pleased to participate in the effort, being addicted mock objects users ourselves! that's really great to hear ! :o) Welcome aboard. I fear we haven't had as much time as we would like so far and any help is welcome. Have a look at CVS and choose what you'd like to do. Unfortunately we've not made a todo list yet. Maybe you could propose items for the todo list and volunteer for some of them ? These are the different areas where you could help : - improve existing code and especially code that is not finished. The servlet mocks for example are far from being finished. - improve build process - improve web site documentation - help answer to mailing-lists posts - promote mock objects around you (I'm sure you're already doing that !) - anything else you might think of Regards, Vincent Tencé -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting |
From: Vincent M. <vm...@oc...> - 2001-11-04 22:35:54
|
----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Cc: vin...@ge...=20 Sent: Monday, October 22, 2001 2:20 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, It's interesting to discuss the pros and cons of MockObjects vs = in-container testing. We are using Catcus a part of our test strategy = and we had some debates ourselves at the beginning on what was the best = approach to use. We quickly realised though that both approaches were = complementary. indeed, although there are still several possibilities : - MO + Cactus + Acceptance tests : the best solution but this is = probably for the most test-infected ! - MO + Acceptance tests : may be enough but my belief is that containers = will play a more and more important role in the future and integration = testing will become predominant in the global testing strategy [we can = already see this with complex ebusiness applications]. Also, you are = shielded from deployment and production issues which I don't believe is = very good. - Cactus + Acceptance tests : may be enough and focuses on preparing = your application to production. Tests take longer to execute but you can = still run them several times a day. It helps you get control of your = integration environment and start thinking about hard issues. It also = prepares you for stress testing/acceptance testing. When the project began, the domain code was still rather simple and = running tests was not an issue. But as soon as we added mor elogic = (domain, crypto) and we started interfacing with external systems (DBs, = JMS providers), we notices that running tests took longer and longer. = We had to develop simulators for external systems, create tests data for = databases and back-end systems accessed through JMS as well as encrypted = incoming data that would go through our cypto libs. It was rather = frustating to see that frequently our domain code was bug-free but our = simulators or data were corrupted. We measured that 2 times out of 3 = tests failures were not due to our domain code. Worst, we had to fix the = bugs before going on and we found it 2 to 3 times longer fixing those = bugs simply because we had no tests to back up our simulators or test = data! yes, which is why Cactus reuses container objects and which is why Mock = Objects are not stubs (in the sense that they do not implement logic but = externalise it to the test cases). I didnt express myself correctly saying that we encountered = limitations with our in-container testing strategy. It was rather our = test strategy as a whole that proved to be poor. Mock objects solved a = lot of our problems with its capacity to do really fine grained unit = tests. We rewrote some of the critical tests that caused problems (DBs, = JMS, Crypto) and now those tests run correclty 100% of the time. We = still use Cactus, but at a much higher level, to test our use cases. So = its like integration tests on a use case by use case basis. To focus on the discussion Mock vs In-container, in our opinion one of = the big advantage of mock objects is the capacity to write very focused = unit tests and sometimes testing pieces of code it would be difficult to = test otherwise. The tests can run very quickly and independently (most = of the time) of external settings=20 agreed ... ensuring that once they have passed, we dont have to worry about them = anymore.=20 well, not completely true, you're still not sure that your code will run = when deployed and interacting with other domain objects and with the = containers. A side effect of using mock objects was that we ended up writing = better code, thought more in terns of interfaces, removing unecessary = dependencies between domain objects and singletons for instance. 100% true. I'm just realising that if you understand this, the answer I = have given in your other email is probably something well known to you. = I apologise if this is the case ... :) On the other hand, using mo proved to be challenging and it still is. = It took time to understand and use them properly, then it took time to = write our own properly (respecting the concepts). We also had to change = parts of our code to introduce the mock objects, adding factories and = registry so that domain objects implementations can be replaced by mock = objects implementations during testing. But once they are written, like = tests, we can leverage mo on subsequent projects. it is ok to refactor code so that it is easier to be tested. However, = you should be careful that you do not introduce new things that will = change too radically your architecture (like decoupling all objects by = providing a new communication layer between them, that would affect = performances). If it is what you need for your application then fine, = however, if it is just needed because of the tests and not needed for = production, then it is bad ... I would imagine that If you participate in both Catcus and MO, you = also feel that both approaches are complementary :-) indeed. I will be very busy in the next weeks or so on my project, but I can = still spare some free time to help on the project. Let me think of the = best way for me to help. Regards, Vincent -Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9 ; moc...@li...=20 Sent: Thursday, October 18, 2001 4:36 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: moc...@li...=20 Sent: Thursday, October 18, 2001 3:36 AM Subject: [Mockobjects-java-dev] Interested in participating Hi all, First of all thanks for the framework, it is of unvaluable use. = Great work.=20 thanks :) We have been developping servlets and were doing in-container = testing up to recently. The limitations that we encountered forced us to = look for a different testing=20 I would be interested in hearing these as I'm participating to both = Cactus (in-container testing) and Mock Objects. I have my own ideas on = the pros and cons of each but would be very interested to hear from = someone who has really used both approaches, on a real project. approach until we read about mock objects. We used the mock = objects servlet package, extended it for our own use, measured the = benefits then decided to develop a mock objects JMS package (it works = fine and serves us well). Now we plan to convert all our DAO unit tests = to using mock objects. Like with JUnit tests, since we tried mock objects, we have a hard = time doing our tests any different way. So if you guys would accept some = help we would be pleased to participate in the effort, being addicted = mock objects users ourselves! that's really great to hear ! :o) Welcome aboard. I fear we haven't = had as much time as we would like so far and any help is welcome. Have a = look at CVS and choose what you'd like to do. Unfortunately we've not = made a todo list yet. Maybe you could propose items for the todo list = and volunteer for some of them ? These are the different areas where you = could help : - improve existing code and especially code that is not finished. = The servlet mocks for example are far from being finished. - improve build process - improve web site documentation - help answer to mailing-lists posts - promote mock objects around you (I'm sure you're already doing = that !) - anything else you might think of Regards, Vincent Tenc=E9 -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting |
From: Vincent M. <vm...@oc...> - 2001-11-04 22:35:35
|
Hi Vince, I'm copying the list as I think they will find this question of = interest. ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Sent: Thursday, November 01, 2001 12:22 AM Subject: Re: [Mockobjects-java-dev] Interested in participating No pb. I myself have a lot of things to do at the moment. The project = is entering in its final phase and the next month will be pretty crazy. = I am afraid I wont have a lot of free time. We want to develop lots of = mock objects for our domain classes and service classes and rewrite our = tests using them.=20 use cactus, you don't need to write mocks ... No, I'm just teasing ... ! = :) A quick question. We are always facing the same issue and I would like = to know what is the approach you would recommend. The question is, how = do we substitute our domain objects for mock implementations at runtime = ? Here is what we do currently: 1) We define our domain/service objects in terms of abstract classes = or preferably interfaces 2) We add a Facade (for domain packages or service packages for = instance) or a Factory (i.e. for DAOs) to create or access instances of = objects 3) We have a kind of JNDI registry that we lookup at runtime for = components by role (like a specific service provider), roles are defined = in terms of interfaces We can then setup our tests by binding to the registry mock = implementations of specific components (roles) and our code under test = looks the registry up and gets mock objects. I suppose you have faced = this pb before. What was your approach ? What I don't like too much witht this approach is that for testing = consideration, you're going to suffer performance penalties and = implement something more complex than it should be (I may not be = understanding correctly what you said) : - each call from one class to another will have to go through JNDI, = instead of the standard intra JVM method to method call. Yes, you can = improve the performance a bit by providing a facade/factory that would = cache the objects but then you'll need to provide your own garbage = collection mechanism ... What you are doing in effect is replacing the = inter-class communication mechanism by providing a loosely coupled one. = This can be a good architecture *but* at the component level and = certainly not at the class level where you'll suffer horrible = performances ... What are the other options ? Answer : good design. A good design will = try to minimise dependencies between classes. One solution is the IOC = (inversion of control) pattern. In summary it says that instead of = having your class instanciate some domain objects, they are passed to = it. For example, let's imagine that your class to test need to connect to a = database. A first implementation could be : Implementation 1: public class MyClass { public void doSomething() { // some JDBC code } } second implementation: you factor the code using the JDBC API in a = JDBCWrapper class (or better yet, in a JDBCDataAccessService class, = implementing a DataAccessService interface). Now, your doSomething() = method does not use JDBC but rather the DataAccessService class : Implementation 2: public class MyClass { public void doSomething() { DataAccess dataService =3D new JDBCDataAccess(); // code that uses dataService } } This is better but not enough to be able to unit test MyClass. What we = need is the possibility to replace JDBCDataAccess by a mock = implementation. Instead of creating the object in doSomething(), you're = going to let the caller pass the implementation to MyClass ... The = result: your MyClass is now more generic and able to work with JDBC, = with an OODB, with flat files, ... and with mocks ... Implementation 3: public class MyClass { private DataAccessService service; public void setDataAccessService(DataAccessService service) { this.service =3D service; } public void doSomething() { // codde that uses this.service } } Note: if you want to be sure that setDataAccessService has been called, = put it as a parameter of your constructor (or of the doSomething() = method). Now, your unit test : public void testDoSomething() { // init MockDataAccessService service =3D new MockDataAccessService(); MyClass class =3D new MyClass(); class.setDataAccessService(service); =20 // test class.doSomething(); // verify assertEquals( .....); } -Vincent PS: I hope I have understood your question ! Thanks, Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9=20 Sent: Wednesday, October 31, 2001 6:49 PM Subject: Re: [Mockobjects-java-dev] Interested in participating Vince, I just realized I have not answered to you. Sorry about that = ! can you wait 2 more days ... will do that this coming week end (It = needs to thinking ... :) ). thanks cheers, -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: Vincent Massol=20 Cc: vin...@ge...=20 Sent: Monday, October 22, 2001 2:20 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, It's interesting to discuss the pros and cons of MockObjects vs = in-container testing. We are using Catcus a part of our test strategy = and we had some debates ourselves at the beginning on what was the best = approach to use. We quickly realised though that both approaches were = complementary. When the project began, the domain code was still rather simple = and running tests was not an issue. But as soon as we added mor elogic = (domain, crypto) and we started interfacing with external systems (DBs, = JMS providers), we notices that running tests took longer and longer. = We had to develop simulators for external systems, create tests data for = databases and back-end systems accessed through JMS as well as encrypted = incoming data that would go through our cypto libs. It was rather = frustating to see that frequently our domain code was bug-free but our = simulators or data were corrupted. We measured that 2 times out of 3 = tests failures were not due to our domain code. Worst, we had to fix the = bugs before going on and we found it 2 to 3 times longer fixing those = bugs simply because we had no tests to back up our simulators or test = data! I didnt express myself correctly saying that we encountered = limitations with our in-container testing strategy. It was rather our = test strategy as a whole that proved to be poor. Mock objects solved a = lot of our problems with its capacity to do really fine grained unit = tests. We rewrote some of the critical tests that caused problems (DBs, = JMS, Crypto) and now those tests run correclty 100% of the time. We = still use Cactus, but at a much higher level, to test our use cases. So = its like integration tests on a use case by use case basis. To focus on the discussion Mock vs In-container, in our opinion = one of the big advantage of mock objects is the capacity to write very = focused unit tests and sometimes testing pieces of code it would be = difficult to test otherwise. The tests can run very quickly and = independently (most of the time) of external settings ensuring that once = they have passed, we dont have to worry about them anymore. A side = effect of using mock objects was that we ended up writing better code, = thought more in terns of interfaces, removing unecessary dependencies = between domain objects and singletons for instance. On the other hand, using mo proved to be challenging and it still = is. It took time to understand and use them properly, then it took time = to write our own properly (respecting the concepts). We also had to = change parts of our code to introduce the mock objects, adding factories = and registry so that domain objects implementations can be replaced by = mock objects implementations during testing. But once they are written, = like tests, we can leverage mo on subsequent projects. I would imagine that If you participate in both Catcus and MO, you = also feel that both approaches are complementary :-) I will be very busy in the next weeks or so on my project, but I = can still spare some free time to help on the project. Let me think of = the best way for me to help. Regards, Vincent ----- Original Message -----=20 From: Vincent Massol=20 To: Vincent Tenc=E9 ; moc...@li... = Sent: Thursday, October 18, 2001 4:36 AM Subject: Re: [Mockobjects-java-dev] Interested in participating Hi Vince, ----- Original Message -----=20 From: Vincent Tenc=E9=20 To: moc...@li...=20 Sent: Thursday, October 18, 2001 3:36 AM Subject: [Mockobjects-java-dev] Interested in participating Hi all, First of all thanks for the framework, it is of unvaluable = use. Great work.=20 thanks :) We have been developping servlets and were doing in-container = testing up to recently. The limitations that we encountered forced us to = look for a different testing=20 I would be interested in hearing these as I'm participating to = both Cactus (in-container testing) and Mock Objects. I have my own ideas = on the pros and cons of each but would be very interested to hear from = someone who has really used both approaches, on a real project. approach until we read about mock objects. We used the mock = objects servlet package, extended it for our own use, measured the = benefits then decided to develop a mock objects JMS package (it works = fine and serves us well). Now we plan to convert all our DAO unit tests = to using mock objects. Like with JUnit tests, since we tried mock objects, we have a = hard time doing our tests any different way. So if you guys would accept = some help we would be pleased to participate in the effort, being = addicted mock objects users ourselves! that's really great to hear ! :o) Welcome aboard. I fear we = haven't had as much time as we would like so far and any help is = welcome. Have a look at CVS and choose what you'd like to do. = Unfortunately we've not made a todo list yet. Maybe you could propose = items for the todo list and volunteer for some of them ? These are the = different areas where you could help : - improve existing code and especially code that is not = finished. The servlet mocks for example are far from being finished. - improve build process - improve web site documentation - help answer to mailing-lists posts - promote mock objects around you (I'm sure you're already doing = that !) - anything else you might think of Regards, Vincent Tenc=E9 -Vincent -- Vincent Massol, vm...@oc... OCTO Technology, www.octo.com Information System Architecture Consulting |
From: Brekke, J. <Jef...@qg...> - 2001-11-04 03:25:27
|
FYI: Uploaded to the patch tracker as #477924 Summary: Updated the build.xml to skip compiling the jms mocks if the javax.jms.Message class cannot be found in the classpath, much like how the servlet stuff is skipped. Changed the order of build.properties processing to read the user's build.properties before the project's build.properties. This way you can define locations and use them in the specific props for projects. Added a commented out property lib.dir to the build.properties.sample to show that the entire lib directory could be changed with a small setting in your local build.properties. |
From: Steve F. <sm...@us...> - 2001-11-03 00:42:09
|
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/jms In directory usw-pr-cvs1:/tmp/cvs-serv24101 Modified Files: MockQueue.java Log Message: added hashCode and toString methods Index: MockQueue.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/jms/MockQueue.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- MockQueue.java 2001/11/03 00:34:07 1.1 +++ MockQueue.java 2001/11/03 00:42:06 1.2 @@ -1,39 +1,48 @@ package com.mockobjects.jms; -import com.mockobjects.*; -import javax.jms.*; +import javax.jms.JMSException; +import javax.jms.Queue; +import com.mockobjects.MockObject; public class MockQueue extends MockObject implements Queue { - private JMSException myException; - private String myName; + private JMSException myException; + private String myName; - public MockQueue(String name) { - myName = name; - } - - public String getQueueName() throws JMSException { - throwExceptionIfAny(); - return myName; - } - - public boolean equals(Object object) { - if (!(object instanceof Queue)) return false; - try { - return myName.equals(((Queue)object).getQueueName()); - } catch (JMSException e) { - throw new junit.framework.AssertionFailedError( - "JMSException caught while getting actual queue name"); - } - } - - public void setupThrowException(JMSException e) { - myException = e; - } - - protected void throwExceptionIfAny() throws JMSException { - if (null != myException) { - throw myException; + public MockQueue(String name) { + myName = name; } - } + + public String getQueueName() throws JMSException { + throwExceptionIfAny(); + return myName; + } + + public boolean equals(Object object) { + if (!(object instanceof Queue)) return false; + try { + return myName.equals(((Queue) object).getQueueName()); + } catch (JMSException e) { + throw new junit.framework.AssertionFailedError( + "JMSException caught while getting actual queue name"); + } + } + + public int hashCode() { + return myName.hashCode(); + } + + public String toString() { + return this.getClass().getName() + ", " + myName; + } + + public void setupThrowException(JMSException e) { + myException = e; + } + + protected void throwExceptionIfAny() throws JMSException { + if (null != myException) { + throw myException; + } + } } |
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/jms In directory usw-pr-cvs1:/tmp/cvs-serv22969 Added Files: MockConnection.java MockMessage.java MockMessageConsumer.java MockMessageProducer.java MockQueue.java MockQueueConnection.java MockQueueConnectionFactory.java MockQueueReceiver.java MockQueueSender.java MockQueueSession.java MockSession.java MockTemporaryQueue.java MockTextMessage.java Log Message: new jms library --- NEW FILE: MockConnection.java --- package com.mockobjects.jms; import javax.jms.Connection; import javax.jms.ConnectionMetaData; import javax.jms.ExceptionListener; import javax.jms.JMSException; import com.mockobjects.ExpectationCounter; import com.mockobjects.MockObject; public class MockConnection extends MockObject implements Connection { protected ExpectationCounter myCloseCalls = new ExpectationCounter("MockConnection.close"); protected ExpectationCounter myStartCalls = new ExpectationCounter("MockConnection.start"); protected ExpectationCounter myStopCalls = new ExpectationCounter("MockConnection.stop"); private JMSException myException; public MockConnection() { } public void close() throws JMSException { myCloseCalls.inc(); throwExceptionIfAny(); } public String getClientID() throws JMSException { notImplemented(); return null; } public ExceptionListener getExceptionListener() throws JMSException { notImplemented(); return null; } public ConnectionMetaData getMetaData() throws JMSException { notImplemented(); return null; } public void setClientID(String clientID) throws JMSException { notImplemented(); } public void setExceptionListener(ExceptionListener listener) throws JMSException { //does nothing } public void start() throws JMSException { myStartCalls.inc(); throwExceptionIfAny(); } public void stop() throws JMSException { myStopCalls.inc(); throwExceptionIfAny(); } public void setExpectedCloseCalls(int callCount) { myCloseCalls.setExpected(callCount); } public void setExpectedStartCalls(int callCount) { myStartCalls.setExpected(callCount); } public void setExpectedStopCalls(int callCount) { myStopCalls.setExpected(callCount); } public void setupThrowException(JMSException e) { myException = e; } protected void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockMessage.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public abstract class MockMessage extends MockObject implements Message { protected ExpectationValue myJMSReplyTo = new ExpectationValue("MockMessage.setJMSReplyTo"); protected ExpectationCounter mySetJMSCorrelationIDCalls = new ExpectationCounter("MockMessage.setJMSCorrelationID"); private JMSException myException; /** * Used for both messageID and correlationID */ private String myJMSMessageID; public MockMessage() { } public void acknowledge() throws JMSException { notImplemented(); } public void clearBody() { notImplemented(); } public void clearProperties() { notImplemented(); } public boolean getBooleanProperty(String name) { notImplemented(); return false; } public byte getByteProperty(String name) { notImplemented(); return 0; } public double getDoubleProperty(String name) { notImplemented(); return 0; } public float getFloatProperty(String name) { notImplemented(); return 0; } public int getIntProperty(String name) { notImplemented(); return 0; } public String getJMSCorrelationID() { return myJMSMessageID; } public byte[] getJMSCorrelationIDAsBytes() { notImplemented(); return null; } public int getJMSDeliveryMode() { notImplemented(); return 0; } public Destination getJMSDestination() { notImplemented(); return null; } public long getJMSExpiration() { notImplemented(); return 0; } public String getJMSMessageID() { return myJMSMessageID; } public int getJMSPriority() { notImplemented(); return 0; } public boolean getJMSRedelivered() { notImplemented(); return false; } public Destination getJMSReplyTo() { notImplemented(); return null; } public long getJMSTimestamp() { notImplemented(); return 0; } public String getJMSType() { notImplemented(); return null; } public long getLongProperty(String name) { notImplemented(); return 0; } public Object getObjectProperty(String name) { notImplemented(); return null; } public java.util.Enumeration getPropertyNames() { notImplemented(); return null; } public short getShortProperty(String name) { notImplemented(); return 0; } public String getStringProperty(String name) { notImplemented(); return null; } public boolean propertyExists(String name) { notImplemented(); return false; } public void setBooleanProperty(String name, boolean value) { notImplemented(); } public void setByteProperty(String name, byte value) { notImplemented(); } public void setDoubleProperty(String name, double value) { notImplemented(); } public void setFloatProperty(String name, float value) { notImplemented(); } public void setIntProperty(String name, int value) { notImplemented(); } public void setJMSCorrelationID(String jmsCorrelationID) { mySetJMSCorrelationIDCalls.inc(); } public void setJMSCorrelationIDAsBytes(byte[] correlationID) { notImplemented(); } public void setJMSDeliveryMode(int deliveryMode) { notImplemented(); } public void setJMSDestination(Destination destination) { notImplemented(); } public void setJMSExpiration(long expiration) { notImplemented(); } public void setJMSMessageID(String id) { notImplemented(); } public void setJMSPriority(int priority) { notImplemented(); } public void setJMSRedelivered(boolean redelivered) { notImplemented(); } public void setJMSReplyTo(Destination replyTo) throws JMSException { throwExceptionIfAny(); myJMSReplyTo.setActual(replyTo); } public void setJMSTimestamp(long timestamp) { notImplemented(); } public void setJMSType(String type) { notImplemented(); } public void setLongProperty(String name, long value) { notImplemented(); } public void setObjectProperty(String name, Object value) { notImplemented(); } public void setShortProperty(String name, short value) { notImplemented(); } public void setStringProperty(String name, String value) { notImplemented(); } public void setExpectedJMSReplyTo(Destination expectedJMSReplyTo) { myJMSReplyTo.setExpected(expectedJMSReplyTo); } public void setExpectedSetJMSCorrelationIDCalls(int callCount) { mySetJMSCorrelationIDCalls.setExpected(callCount); } public void setupJMSMessageID(String jmsMessageID) { myJMSMessageID = jmsMessageID; } public void setupThrowException(JMSException e) { myException = e; } protected void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockMessageConsumer.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public abstract class MockMessageConsumer extends MockObject implements MessageConsumer { protected ExpectationCounter myCloseCalls = new ExpectationCounter("MockMessageConsumer.close"); protected ExpectationCounter myReceiveCalls = new ExpectationCounter("MockMessageConsumer.receive"); private Message myMessage; private boolean myExpiresOnTimeout = false; private JMSException myException; public MockMessageConsumer() { } public void close() throws JMSException { throwExceptionIfAny(); myCloseCalls.inc(); } public MessageListener getMessageListener() throws JMSException { notImplemented(); return null; } public String getMessageSelector() throws JMSException { notImplemented(); return null; } public Message receive() throws JMSException { throwExceptionIfAny(); myReceiveCalls.inc(); if (myExpiresOnTimeout) { synchronized(this) { try { wait(); } catch (InterruptedException e) { throw new junit.framework.AssertionFailedError("Thread interrupted"); } } } return myMessage; } public Message receive(long timeout) throws JMSException { throwExceptionIfAny(); myReceiveCalls.inc(); if (myExpiresOnTimeout) { return null; } else { return myMessage; } } public Message receiveNoWait() throws JMSException { throwExceptionIfAny(); myReceiveCalls.inc(); return myMessage; } public void setMessageListener(MessageListener listener) throws JMSException { notImplemented(); } public void setExpectedCloseCalls(int callCount) { myCloseCalls.setExpected(callCount); } public void setExpectedReceiveCalls(int callCount) { myReceiveCalls.setExpected(callCount); } public void setupReceivedMessage(Message message) { myMessage = message; } public void setupExpiresOnTimeout(boolean expiresOnTimeout) { myExpiresOnTimeout = expiresOnTimeout; } public void setupThrowException(JMSException e) { myException = e; } protected void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockMessageProducer.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public abstract class MockMessageProducer extends MockObject implements MessageProducer { protected ExpectationCounter myCloseCalls = new ExpectationCounter("MockMessageConsumer.close"); private JMSException myException; public MockMessageProducer() { } public void close() throws JMSException { myCloseCalls.inc(); } public int getDeliveryMode() throws JMSException { notImplemented(); return 0; } public boolean getDisableMessageID() throws JMSException { notImplemented(); return false; } public boolean getDisableMessageTimestamp() throws JMSException { notImplemented(); return false; } public int getPriority() throws JMSException { notImplemented(); return 0; } public long getTimeToLive() throws JMSException { notImplemented(); return 0l; } public void setDeliveryMode(int deliveryMode) throws JMSException { notImplemented(); } public void setDisableMessageID(boolean value) throws JMSException { notImplemented(); } public void setDisableMessageTimestamp(boolean value) throws JMSException { notImplemented(); } public void setPriority(int defaultPriority) throws JMSException { notImplemented(); } public void setTimeToLive(long timeToLive) throws JMSException { notImplemented(); } public void setExpectedCloseCalls(int callCount) { myCloseCalls.setExpected(callCount); } public void setupThrowException(JMSException e) { myException = e; } protected void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockQueue.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockQueue extends MockObject implements Queue { private JMSException myException; private String myName; public MockQueue(String name) { myName = name; } public String getQueueName() throws JMSException { throwExceptionIfAny(); return myName; } public boolean equals(Object object) { if (!(object instanceof Queue)) return false; try { return myName.equals(((Queue)object).getQueueName()); } catch (JMSException e) { throw new junit.framework.AssertionFailedError( "JMSException caught while getting actual queue name"); } } public void setupThrowException(JMSException e) { myException = e; } protected void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockQueueConnection.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockQueueConnection extends MockConnection implements QueueConnection { private QueueSession myQueueSession; public MockQueueConnection() { } public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, ServerSessionPool sessionPool, int mexMessages) throws JMSException { notImplemented(); return null; } public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException { throwExceptionIfAny(); return myQueueSession; } public void setupQueueSession(QueueSession queueSession) { myQueueSession = queueSession; } } --- NEW FILE: MockQueueConnectionFactory.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockQueueConnectionFactory extends MockObject implements QueueConnectionFactory { protected ExpectationValue myUserName = new ExpectationValue("MockQueueConnectionFactory.createQueueConnection"); protected ExpectationValue myPassword = new ExpectationValue("MockQueueConnectionFactory.createQueueConnection"); private JMSException myException; private QueueConnection myQueueConnection; public MockQueueConnectionFactory() { } public QueueConnection createQueueConnection() throws JMSException { throwExceptionIfAny(); return myQueueConnection; } public QueueConnection createQueueConnection(String userName, String password) throws JMSException { throwExceptionIfAny(); myUserName.setActual(userName); myPassword.setActual(password); return myQueueConnection; } public void setExpectedUserName(String userName) { myUserName.setExpected(userName); } public void setExpectedPassword(String password) { myPassword.setExpected(password); } public void setupQueueConnection(QueueConnection queueConnection) { myQueueConnection = queueConnection; } public void setupThrowException(JMSException e) { myException = e; } private void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockQueueReceiver.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockQueueReceiver extends MockMessageConsumer implements QueueReceiver { public MockQueueReceiver() { } public Queue getQueue() { notImplemented(); return null; } } --- NEW FILE: MockQueueSender.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockQueueSender extends MockMessageProducer implements QueueSender { protected ExpectationCounter mySendCalls = new ExpectationCounter("MockQueueSender.send"); public MockQueueSender() { } public Queue getQueue() throws JMSException { notImplemented(); return null; } public void send(Message message) throws JMSException { mySendCalls.inc(); throwExceptionIfAny(); } public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { notImplemented(); } public void send(Queue queue, Message message) throws JMSException { notImplemented(); } public void send(Queue queue, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { notImplemented(); } public void setExpectedSendCalls(int callCount) { mySendCalls.setExpected(callCount); } } --- NEW FILE: MockQueueSession.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockQueueSession extends MockSession implements QueueSession { protected ExpectationValue mySendingQueue = new ExpectationValue("MockQueueSession.createSender"); protected ExpectationValue myReceivingQueue = new ExpectationValue("MockQueueSession.createReceiver"); private QueueReceiver myReceiver; private QueueSender mySender; private TemporaryQueue myTemporaryQueue; public MockQueueSession() { } public QueueBrowser createBrowser(Queue queue) throws JMSException { notImplemented(); return null; } public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException { notImplemented(); return null; } public Queue createQueue(String queueName) throws JMSException { throwExceptionIfAny(); return new MockQueue(queueName); } public QueueReceiver createReceiver(Queue queue) throws JMSException { throwExceptionIfAny(); myReceivingQueue.setActual(queue); return myReceiver; } public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { myReceivingQueue.setActual(queue); throwExceptionIfAny(); return myReceiver; } public QueueSender createSender(Queue queue) throws JMSException { mySendingQueue.setActual(queue); throwExceptionIfAny(); return mySender; } public TemporaryQueue createTemporaryQueue() throws JMSException { throwExceptionIfAny(); return myTemporaryQueue; } public void setExpectedSendingQueue(Queue queue) { mySendingQueue.setExpected(queue); } public void setExpectedReceivingQueue(Queue queue) { myReceivingQueue.setExpected(queue); } public void setupReceiver(QueueReceiver receiver) { myReceiver = receiver; } public void setupSender(QueueSender sender) { mySender = sender; } public void setupTemporaryQueue(MockTemporaryQueue temporaryQueue) { myTemporaryQueue = temporaryQueue; } } --- NEW FILE: MockSession.java --- package com.mockobjects.jms; import com.mockobjects.*; import java.io.Serializable; import javax.jms.*; public class MockSession extends MockObject implements Session { protected ExpectationCounter myCloseCalls = new ExpectationCounter("MockSession.close"); protected ExpectationCounter myCreateTextMessageCalls = new ExpectationCounter("MockSession.createTextMessage"); private TextMessage myTextMessage = new MockTextMessage(); private JMSException myException; public MockSession() { } public void close() throws JMSException { throwExceptionIfAny(); myCloseCalls.inc(); } public void commit() throws JMSException { notImplemented(); } public BytesMessage createBytesMessage() throws JMSException { notImplemented(); return null; } public MapMessage createMapMessage() throws JMSException { notImplemented(); return null; } public Message createMessage() throws JMSException { notImplemented(); return null; } public ObjectMessage createObjectMessage() throws JMSException { notImplemented(); return null; } public ObjectMessage createObjectMessage(Serializable object) throws JMSException { notImplemented(); return null; } public StreamMessage createStreamMessage() throws JMSException { notImplemented(); return null; } public TextMessage createTextMessage() throws JMSException { myCreateTextMessageCalls.inc(); return myTextMessage; } public TextMessage createTextMessage(String text) throws JMSException { myTextMessage.setText(text); return myTextMessage; } public MessageListener getMessageListener() throws JMSException { notImplemented(); return null; } public boolean getTransacted() throws JMSException { notImplemented(); return false; } public void recover() throws JMSException { notImplemented(); } public void rollback() throws JMSException { notImplemented(); } public void run() { notImplemented(); } public void setMessageListener(MessageListener listener) throws JMSException { notImplemented(); } public void setExpectedCloseCalls(int callCount) { myCloseCalls.setExpected(callCount); } public void setExpectedCreateTextMessageCalls(int callCount) { myCreateTextMessageCalls.setExpected(callCount); } public void setupTextMessage(TextMessage textMessage) { myTextMessage = textMessage; } public void setupThrowException(JMSException e) { myException = e; } protected void throwExceptionIfAny() throws JMSException { if (null != myException) { throw myException; } } } --- NEW FILE: MockTemporaryQueue.java --- package com.mockobjects.jms; import javax.jms.JMSException; import javax.jms.TemporaryQueue; import com.mockobjects.ExpectationCounter; public class MockTemporaryQueue extends MockQueue implements TemporaryQueue { protected ExpectationCounter myDeleteCalls = new ExpectationCounter("MockTemporaryQueue.delete"); public MockTemporaryQueue() { super("TemporaryQueue"); } public void delete() throws JMSException { myDeleteCalls.inc(); throwExceptionIfAny(); } public void setExpectedDeleteCalls(int callCount) { myDeleteCalls.setExpected(callCount); } } --- NEW FILE: MockTextMessage.java --- package com.mockobjects.jms; import com.mockobjects.*; import javax.jms.*; public class MockTextMessage extends MockMessage implements TextMessage { protected ExpectationValue myExpectedText = new ExpectationValue("MockTextMessage.setText"); private String myText; public MockTextMessage() { } public MockTextMessage(String text) { myText = text; } public void setText(String text) { myExpectedText.setActual(text); } public String getText() { return myText; } public void setExpectedText(String text) { myExpectedText.setExpected(text); } } |
From: Steve F. <sm...@us...> - 2001-11-03 00:33:34
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv22896 Modified Files: brief_introduction.html Log Message: more Index: brief_introduction.html =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers/brief_introduction.html,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- brief_introduction.html 2001/10/27 23:37:05 1.2 +++ brief_introduction.html 2001/11/03 00:33:31 1.3 @@ -2,9 +2,9 @@ <html> <head> <title>Developing JDBC applications test-first</title> - + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - + <style type="text/css"> <!-- .deemphasised { color: #666666} @@ -87,7 +87,7 @@ assertEquals("Should be destination", DESTINATION, robot.getPosition()); List moves = robot.getRecentMoveRequests(); assertEquals("Should be one move", 1, moves.size()); - assertEquals("Should be same move", + assertEquals("Should be same move", new MoveRequest(1, MoveRequest.SOUTH), moves.get(1)); }</pre> @@ -100,7 +100,7 @@ robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); - assertEquals("Should be same moves", + assertEquals("Should be same moves", makeExpectedLongWayMoves(), robot.getRecentMoves()); }</pre> @@ -149,8 +149,8 @@ }</pre> <p>I can now refactor my tests and replace the real Motor with a -MockMotor that can watch what's happening in the Robot and complain as -soon as something goes wrong. My first test is now:</p> +MockMotor that can watch what's happening in the Robot, and complain as +soon as something goes wrong. The first test is now:</p> <pre>public void testGotoSamePlace() { final Position POSITION = new Position(0, 0); @@ -162,7 +162,7 @@ } }; robot.setMotor(mockMotor); - + robot.goTo(POSITION); assertEquals("Should be same", POSITION, robot.getPosition()); @@ -170,16 +170,16 @@ <p>In this test, if there is a bug in the Robot code and the Motor gets requested to move, the mock implementation of <em>move()</em> -will simply fail and stop the test. I no longer need to ask the Robot -where it's been. I can write a more sophisticated MockMotor that works -for multiple requests and use it throughout all the Robot tests; for -example:</p> +will fail immediately and stop the test; I no longer need to ask the Robot +where it's been. As my tests grow, I can refactor the various mock implementations +into a single, more sophisticated MockMotor and use it throughout all the Robot +tests; for example:</p> <pre>public void MockMotor implements Motor { private ArrayList expectedRequests = new ArrayList(); public void move(MoveRequest request) { assert("Too many requests", this.expectedRequests.size() > 0); - assertEquals("Should be next request", + assertEquals("Should be next request", this.expectedRequests.remove(0), request); } public void addExpectedRequest(MoveRequest request) { @@ -198,7 +198,7 @@ public void setUp() { mockMotor = new MockMotor(); robot = new Robot(mockMotor); - robot.setCurrentPosition(POSITION); + robot.setCurrentPosition(ORIGIN); } public void testGotoSamePlace() { robot.goTo(ORIGIN); @@ -216,12 +216,34 @@ assertEquals("Should be destination", DESTINATION, robot.getPosition()); mockMotor.verify(); } + public void testMoveALongWay() { + final Position DESTINATION = new Position(34, 71); + + mockMotor.addExpectedRequests(makeExpectedLongWayMoveRequests()); + + robot.goto(DESTINATION); + + assertEquals("Should be destination", DESTINATION, robot.getPosition()); + mockMotor.verify(); }</pre> -<p>What has this change achieved? -</p> +<h2>What has this change achieved?</h2> +<ul> + <li><em>Refactored test infrastructure</em> As my tests grow, I find myself repeating my test + infrastructure. I can push those repetitions into mock implementations, based on the + expectation types that ship with the Mock Objects library. </li> + <li><em>Failing early</em> A key experience of developing test-first is learning to make test + failures self explanatory. Failing at the time an error occurs with a useful message, + rather than at the end of the test, is a powerful tool.</li> + <li><em>Driving design</em> Choosing which objects to verify in a test turns out to be an + effective tool for deriving the structure of a class and its neighbours.</li> + <li><em>Consistent test structure</em> Most unit tests in this style have a similar structure: + set up the objects (pre-conditions), set the expected behaviour (invariants and + post-conditions), exercise the object, and then verify whether the expectations were + fufilled. This style makes tests easy to work with because they look similar and because + all the interactions with an object are local to a test fixture.</li> +</ul> <hr> -<p>© Steve Freeman, 2001</p> -<p> </p> +<p>© Steve Freeman 2001</p> </body> </html> |
From: Steve F. <sm...@us...> - 2001-11-03 00:32:37
|
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/jms In directory usw-pr-cvs1:/tmp/cvs-serv22728/jms Log Message: Directory /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/jms added to the repository |
From: Steve F. <sm...@us...> - 2001-10-27 23:37:09
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv4667 Modified Files: brief_introduction.html Log Message: Changed example in introduction paper Index: brief_introduction.html =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers/brief_introduction.html,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- brief_introduction.html 2001/09/24 23:40:22 1.1 +++ brief_introduction.html 2001/10/27 23:37:05 1.2 @@ -1,110 +1,225 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> -<title>Developing JDBC applications test-first</title> -<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> -<style type="text/css"> + <title>Developing JDBC applications test-first</title> + + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + + <style type="text/css"> <!-- .deemphasised { color: #666666} h1 { text-align: center; font-family: Arial, Helvetica, sans-serif; font-weight: bold} h3 { font-family: Arial, Helvetica, sans-serif; font-style: italic; font-weight: bold; font-size: small} -.inline_code { font-family: "Courier New", Courier, mono; font-style: normal; font-size: smaller; vertical-align: middle} -p { font-family: Arial, Helvetica, sans-serif} +.inline_code { font-family: "Courier New", Courier, mono; font-style: normal; vertical-align: middle} +p { font-family: Arial, Helvetica, sans-serif; margin-left: 5%} li { font-family: Arial, Helvetica, sans-serif } h2 { font-family: Arial, Helvetica, sans-serif; margin-top: 3%} +pre { margin-left: 7%} --> </style> </head> - -<body bgcolor="#FFFFFF"> -<h1>How Mock Objects happened</h1> -<p align="center">Steve Freeman <tt> <st...@m3...></tt></p> + <body bgcolor="#ffffff"> +<h1>How Mock Objects happen</h1> +<p align="Center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> -<p>Unit testing is a fundamental practice in Extreme Programming, but most nontrivial - code is difficult to test in isolation. You need to make sure that you test - one feature at a time, and you want to be notified as soon as any problem occurs. - Normal unit testing is hard because you are trying to test the code from outside. -</p> -<p>Mock Objects is an approach to writing unit tests based on two key techniques: - replace everything except the code under test with mock implementations that - emulate the rest of the environement, and put the test assertions <em>inside</em> - those mock implementations so that you can validate the interactions between - the objects in your test case.</p> + +<p>Unit testing is a fundamental practice in Extreme Programming, but +most nontrivial code is difficult to test in isolation. You need to +make sure that you test one feature at a time, and you want to be +notified as soon as any problem occurs. Normal unit testing is hard +because you are trying to test the code from outside. </p> + +<p>Mock Objects is an approach to writing unit tests based on two key +techniques: replace everything except the code under test with mock +implementations that emulate the rest of the environement, and put the +test assertions <em>inside</em> those mock implementations so that you +can validate the interactions between the objects in your test +case.</p> + +<p>This article assumes that you are familiar with java and unit testing using JUnit</p> + <h2>Simple unit tests</h2> -<p>Most people who start writing unit tests by calling some methods and checking - some of the state of the object under test. For example, a test for a simple - calculator object might be:</p> -<pre> public void setUp() { - this.calculator = new Calculator(); - } - - public void testAdd() { - this.calculator.set(5); - this.calculator.add(3); - assertEquals("Should be addition", 8, this.calculator.getResult()); - } -</pre> -<p>As a class becomes more complex, this style of testing becomes harder to maintain. - For example, if our calculator now keeps a history of the calculations it makes, - a test might be:</p> -<pre> public void testHistory() { - this.calculator.set(5); - this.calculator.add(3); - - - Vector calcuations = new Vector(); - calcuations.add(new Calculation(Calculation.SET, 5)); - calcuations.add(new Calculation(Calculation.ADD, 3)); - assertEquals("Should be equal calculations", - calcuations, this.calculator.getHistory()); - - } -</pre> -<p>The problem with this approach is that we must either return the actual history - data structure, which limits our choice of implementation, or return a vector - copy, which is expensive. Furthermore, it is impossible to tell which choice - we have made from the test. To hide the underlying collection, the next step - is to return an <span class="inline_code">Iterator</span> for the calculation - history, rather than the collection itself. Our test now becomes:</p> -<pre> public void testHistory() { - this.calculator.set(5); - this.calculator.add(3); - - - Vector calcuations = new Vector(); - calcuations.add(new Calculation(Calculation.SET, 5)); - calcuations.add(new Calculation(Calculation.ADD, 3)); - compareIterators(calculations.iterator(), this.calculator.getHistory()); - }</pre> -<p>where <span class="inline_code">compareIterators()</span> is a helper method - that compares the contents of two Iterators. </p> -<p>This approach becomes less manageable as the code becomes still more complex. - For example, we now change our class to perform some lengthy calculation that - takes multiple input values and returns multiple output values:</p> -<pre> public void testLongCalculation() { - Result[] results = - this.calculator.complicatedOperation(makeLongCalculationInputs()); - - - compareResults(makeLongCalculationResults(), results); - }</pre> -<p>To manage the inputs and results, there are helper methods, <span class="inline_code">makeLongCalculationInputs</span> - and <span class="inline_code">makeLongCalculationResults</span>, for constructing - the collections of values. </p> -<p>Tests like this are effectively small-scale integration tests, they set up - pre-conditions and test post-conditions but they have no access to the code - while it is running. When this test fails, we will have to step through the - code to find the problem because the assertions are made <em>after</em> the - calculation is finished. Is there a better way?</p> -<h2>A first Mock Object</h2> -<p>One thing we know about this complicated operation is that we need to accumulate - a set of results that currently are returned from the method. We can factor - out that process of accumulation to a separate object, and write an interface - for it:</p> -<pre> public interface Accumulator { - /**<br> * called once for each result in the calculation - */ - void add(Result result);<br> } </pre> -<p> </p> + +<p>Most people start writing unit tests by creating an object, calling +some methods on it, and then checking its state. For example, imagine +that I'm writing a component automatically to direct robots through a +warehouse. Given a start point on a grid, it will find a route through +the stacks to a given destination. The tests will have to tell me that +the robot arrives correctly and does not hit anything on the way. A +first test might be: </p> + +<pre>public class TestRobot { + [...] + public void setUp() { + robot = new Robot(); + } + public void testGotoSamePlace() { + final Position POSITION = new Position(1, 1); + robot.setCurrentPosition(POSITION); + robot.goTo(POSITION); + + assertEquals("Should be same", POSITION, robot.getPosition()); + } +}</pre> + +<p>This tells me that the robot thinks that it has arrived, but it +doesn't tell me anything about how it got there. I would like to know +that the Robot didn't move in this test. One option would be to store +and retrieve the route it took after each <em>goto()</em>. For +example:</p> + +<pre>public void testGotoSamePlace() { + final Position POSITION = new Position(0, 0); + robot.setCurrentPosition(POSITION); + robot.goTo(POSITION); + + assertEquals("Should be empty", 0, robot.getRecentMoveRequests().size()); + assertEquals("Should be same", POSITION, robot.getPosition()); +}</pre> + +<p>My next test might be to move one point on the grid:</p> + +<pre>public void testMoveOnePoint() { + final Position DESTINATION = new Position(1, 0); + robot.setCurrentPosition(new Position(0, 0)); + robot.goto(DESTINATION); + + assertEquals("Should be destination", DESTINATION, robot.getPosition()); + List moves = robot.getRecentMoveRequests(); + assertEquals("Should be one move", 1, moves.size()); + assertEquals("Should be same move", + new MoveRequest(1, MoveRequest.SOUTH), moves.get(1)); +}</pre> + +<p>As the tests become more complex, I would pull out some of the +detail into helper methods to make the code more legible:</p> + +<pre>public void testMoveALongWay() { + final Position DESTINATION = new Position(34, 71); + robot.setCurrentPosition(new Position(0, 0)); + robot.goto(DESTINATION); + + assertEquals("Should be destination", DESTINATION, robot.getPosition()); + assertEquals("Should be same moves", + makeExpectedLongWayMoves(), robot.getRecentMoves()); +}</pre> + +<p>Where <em>makeExpectedLongWayMoves()</em> returns another list of +the moves I expect the robot to take for this test. </p> + +<p>There are problems with this approach to testing. First, tests like +this are effectively small-scale integration tests, they set up +pre-conditions and test post-conditions but they have no access to the +code while it is running. If one of these tests fail, I will have to +step through the code to find the problem because the assertions have +been made <em>after</em> the call has finished. Second, to test this +behaviour at all, I had to add some functionality to the production +class, namely retaining all the MoveRequests since the last +<em>goto()</em>. I don't have any other immediate need for +<em>getRecentMovesRequests()</em>. Third, although it's not the case here, +test suites based on extracting history from objects tend to need lots +of utilities for constructing and comparing collections of values. The +need to write external code to manipulate an object is often a warning +that its class is incomplete and that some behaviour should be moved +from the utility to the class.</p> + +<p>Is there a better way? Can I find a style that will give me better +error reporting and put the behaviour in the right place? </p> + +<h2>Factoring out the motor</h2> + +<p>One thing I can be sure of is that the Robot contains a Motor that +responds to these move requests. If I can intercept those requests, I +can track what's happening in the Robot. A first step would be to +define an interface to the Motor:</p> + +<pre>public interface Motor { + void move(MoveRequest request); +}</pre> + +<p>I can pass an instance of Motor to the Robot, perhaps via its +constructor, and a simple implementation might be:</p> + +<pre>public class OneSpeedMotor implements Motor { + public void move(MoveRequest request) { + turnBy(request.getTurn()); + advance(request.getDistance()); + } + [...] +}</pre> + +<p>I can now refactor my tests and replace the real Motor with a +MockMotor that can watch what's happening in the Robot and complain as +soon as something goes wrong. My first test is now:</p> + +<pre>public void testGotoSamePlace() { + final Position POSITION = new Position(0, 0); + robot.setCurrentPosition(POSITION); + + Motor mockMotor = new Motor() { + public void move(MoveRequest request) { + fail("There should be no moves in this test"); + } + }; + robot.setMotor(mockMotor); + + robot.goTo(POSITION); + + assertEquals("Should be same", POSITION, robot.getPosition()); +}</pre> + +<p>In this test, if there is a bug in the Robot code and the Motor +gets requested to move, the mock implementation of <em>move()</em> +will simply fail and stop the test. I no longer need to ask the Robot +where it's been. I can write a more sophisticated MockMotor that works +for multiple requests and use it throughout all the Robot tests; for +example:</p> + +<pre>public void MockMotor implements Motor { + private ArrayList expectedRequests = new ArrayList(); + public void move(MoveRequest request) { + assert("Too many requests", this.expectedRequests.size() > 0); + assertEquals("Should be next request", + this.expectedRequests.remove(0), request); + } + public void addExpectedRequest(MoveRequest request) { + this.expectedRequests.add(request); + } + public void verify() { + assertEquals("Too few requests", 0, this.expectedRequests.size()); + } +}</pre> + +<p>Which makes our tests look like:</p> + +<pre>public class TestRobot { + [...] + static final Position ORIGIN = new Position(0, 0); + public void setUp() { + mockMotor = new MockMotor(); + robot = new Robot(mockMotor); + robot.setCurrentPosition(POSITION); + } + public void testGotoSamePlace() { + robot.goTo(ORIGIN); + + assertEquals("Should be same", ORIGIN, robot.getPosition()); + mockMotor.verify(); + } + public void testMoveOnePoint() { + final Position DESTINATION = new Position(1, 0); + + mockRobot.addExpectedRequest(new MoveRequest(1, MoveRequest.SOUTH)); + + robot.goto(DESTINATION); + + assertEquals("Should be destination", DESTINATION, robot.getPosition()); + mockMotor.verify(); + } +}</pre> + +<p>What has this change achieved? +</p> <hr> <p>© Steve Freeman, 2001</p> <p> </p> |
From: Lucas F. <lu...@ha...> - 2001-10-25 13:16:10
|
High all, high Vincent Tenc ! you wrote in your recent msg: SUBJECT: [Mockobjects-java-dev] Interested in participating : you wrote: > ... then decided to > develop a mock objects JMS package (it works fine and serves us well). I am absolutely interested in this since for our current project we have to integrate JMS now. It would help us a lot! Could you send me the JMS mock package or make it publicly available, maybe on this site? Thank you a lot, Lucas |