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: Vincent M. <vm...@oc...> - 2001-10-18 08:27:34
|
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. <st...@ok...> - 2001-10-18 06:03:44
|
Vincent Tencé <vt...@sy...> said: > > First of all thanks for the framework, it is of unvaluable use. > Great work. Thanks for the compliment (we're blushing...) > > 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 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! Obviously, we would be interested in seeing your JMS library, and we'd be very interested in hearing about your metrics for the benefits. Otherwise, whatever else you would be interested in contributing. Steve |
From: <vt...@sy...> - 2001-10-18 02:38:33
|
Hi all, First of all thanks for the framework, it is of unvaluable use. Great = work.=20 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 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! Regards, Vincent Tenc=E9 |
From: Steve F. <sm...@us...> - 2001-10-11 18:22:31
|
Update of /cvsroot/mockobjects/mockobjects-java/src/extensions/com/mockobjects/atg In directory usw-pr-cvs1:/tmp/cvs-serv23007 Modified Files: MockRepository.java Log Message: added repository items Index: MockRepository.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/extensions/com/mockobjects/atg/MockRepository.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- MockRepository.java 2001/07/29 19:50:25 1.1 +++ MockRepository.java 2001/10/11 18:22:23 1.2 @@ -1,40 +1,33 @@ package com.mockobjects.atg; -import atg.repository.*;/** - * Creation date: (05/04/2001 18:13:21) - * @author: - */ -public class MockRepository implements Repository { + +import atg.repository.*; +import com.mockobjects.ExpectationMap; +import com.mockobjects.Verifiable; +import com.mockobjects.MockObject; +import com.mockobjects.ExpectationValue; + +public class MockRepository extends MockObject implements Repository, Verifiable { RepositoryException exception; RepositoryItemDescriptor itemDescriptor; + ExpectationMap items = new ExpectationMap("MockRepository items"); - /** - * MockRepository constructor comment. - */ public MockRepository() { super(); } - /** - * getDefaultViewName method comment. - */ public String getDefaultViewName() { return null; } - /** - * getItem method comment. - */ - public RepositoryItem getItem(String arg1) throws RepositoryException { - if (exception != null) { + public RepositoryItem getItem(String id) throws RepositoryException { + RepositoryItem result = (RepositoryItem)items.get(id); + if (null != exception) { throw exception; } - return null; + return result; } - /** - * getItem method comment. - */ public RepositoryItem getItem(String arg1, String arg2) throws RepositoryException { if (exception != null) { throw exception; @@ -43,9 +36,6 @@ return null; } - /** - * getItemDescriptor method comment. - */ public RepositoryItemDescriptor getItemDescriptor(String arg1) throws RepositoryException { if (exception != null) { throw exception; @@ -54,16 +44,10 @@ return this.itemDescriptor; } - /** - * getItemDescriptorNames method comment. - */ public java.lang.String[] getItemDescriptorNames() { return null; } - /** - * getItems method comment. - */ public atg.repository.RepositoryItem[] getItems(java.lang.String[] arg1) throws RepositoryException { if (exception != null) { throw exception; @@ -72,9 +56,6 @@ return null; } - /** - * getItems method comment. - */ public atg.repository.RepositoryItem[] getItems(java.lang.String[] arg1, String arg2) throws RepositoryException { if (exception != null) { throw exception; @@ -83,16 +64,10 @@ return null; } - /** - * getRepositoryName method comment. - */ public String getRepositoryName() { return null; } - /** - * getView method comment. - */ public RepositoryView getView(String arg1) throws RepositoryException { if (exception != null) { throw exception; @@ -101,26 +76,23 @@ return null; } - /** - * getViewNames method comment. - */ public java.lang.String[] getViewNames() { return null; } - /** - * Creation date: (05/04/2001 18:34:26) - * @param exception atg.repository.RepositoryException - */ public void setupException(RepositoryException exception) { this.exception = exception; } - /** - * Creation date: (05/04/2001 18:34:26) - * @param exception atg.repository.RepositoryException - */ public void setupRepositoryItemDescriptor(RepositoryItemDescriptor itemDescriptor) { this.itemDescriptor = itemDescriptor; + } + + public void addExpectedGetItem(String id, RepositoryItem item) { + items.addExpected(id, item); + } + + public void setExpectNoItems() { + items.setExpectNothing(); } -} \ No newline at end of file +} |
From: Steve F. <sm...@us...> - 2001-09-24 23:40:25
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv30856 Added Files: brief_introduction.html Log Message: early version --- NEW FILE: brief_introduction.html --- <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} 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} li { font-family: Arial, Helvetica, sans-serif } h2 { font-family: Arial, Helvetica, sans-serif; margin-top: 3%} --> </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>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> <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> <hr> <p>© Steve Freeman, 2001</p> <p> </p> </body> </html> |
From: Tim M. <tim...@po...> - 2001-09-23 22:46:58
|
Steve Freeman wrote: > Don't forget that one of the points of a unit test is to be self > explanatory. The risk with pulling test data out of a file is that > it's harder to see what's going on I agree with steve - we have examples of xml data in separate files and those tests are hard to read - but at least you can see a text representation. At least read in comma delimited data - but you know there is a potential smell here... What are your tests doing with so much data that makes it so awkward? At the database level I usually test the boundary conditions for populating a higher level object. I then pass this object around for calculations. This other object has a nicer interface to mock up - in fact often you can put the calculations in it and then just have a mock for it that gives answers to the rest of the system. tim |
From: Steve F. <st...@m3...> - 2001-09-20 22:18:22
|
UkU6IFtNb2Nrb2JqZWN0cy1qYXZhLWRldl0gTXVsdGlwbGUgUmVzdWx0U2V0IHByb2JsZW0+IEZy b206IEdveWFsLCBHYXVyYXYgKEdFQSwgMDk3OTAzKSANCj4gSSdtIHBsYW5uaW5nIHRvIGltcGxl bWVudCB0aGUgY2FwYWJpbGl0eSBvZiBob2xkaW5nIG11bHRpcGxlIHJlc3VsdHNldHMgd2l0aGlu IGEgc3RhdGVtZW50LiANCj4gSXRzIGFjdHVhbGx5IGEgY3J1Y2lhbCBmdW5jdGlvbmFsaXR5IHRo YXQgd2UnbGwgbmVlZCB0byBwdXQgaW4sIGJlZm9yZSB3ZSBnbyBhbGwgb3V0IG9uIHVzaW5nIA0K PiBtb2NrIG9iamVjdHMuIA0KDQpsZXQncyBzZWUgd2hvIGdldHMgdGhlcmUgZmlyc3QgOy0pIA0K DQo+IEFub3RoZXIgdGhpbmcgdGhhdCBJJ20gcGxhbm5pbmcgdG8gZG8gaXMgdG8gbWFrZSB0aGUg TW9ja1Jlc3VsdFNldCBvYmplY3Qgc2VyaWFsaXphYmxlIA0KPiBhbmQgdGhlbiB0byBpbXBsZW1l bnQgc29tZSBraW5kIG9mIGEgc2ltcGxlIEdVSSwgdG8gY3JlYXRlIHRoZXNlIE1vY2tSZXN1bHRT ZXQgb2JqZWN0cy4gDQo+IFRoaXMgSSB0aGluayB3aWxsIHNhdmUgdGltZSB3aGlsZSBhY3R1YWxs eSB3cml0aW5nIHRlc3QgY2FzZXMuIEl0cyBhIGJpdCBwYWluZnVsIHRvIHR5cGUgaW4gdGhlIA0K PiBjb2RlIHRvIGluc3RhbnRpYXRlIHRoZSBNb2NrQ29ubmVjdGlvbixNb2NrU3RhdGVtZW50LCBN b2NrUmVzdWx0U2V0IGFuZCB0aGUgDQo+IE1vY2tSZXN1bHRTZXRNZXRhRGF0YSBpbiBldmVyeSB0 ZXN0LiBXaGF0IGRvIHlvdSB0aGluayBhYm91dCB0aGlzPw0KDQpEb24ndCBmb3JnZXQgdGhhdCBv bmUgb2YgdGhlIHBvaW50cyBvZiBhIHVuaXQgdGVzdCBpcyB0byBiZSBzZWxmIGV4cGxhbmF0b3J5 LiBUaGUgcmlzayB3aXRoIHB1bGxpbmcgdGVzdCBkYXRhIG91dCBvZiBhIGZpbGUgaXMgdGhhdCBp dCdzIGhhcmRlciB0byBzZWUgd2hhdCdzIGdvaW5nIG9uLiBBdCB0aGUgdmVyeSBsZWFzdCwgeW91 IGNhbiBhc3NlbWJsZSB5b3VyIG1vY2tzIGluIHRoZSBzZXRVcCBtZXRob2Qgc28gdGhhdCB5b3Ug ZG9uJ3QgaGF2ZSB0byBkbyBpdCBpbiBldmVyeSB0ZXN0Lg0KDQpXZSBuZWVkIHRvIHRoaW5rIGFi b3V0IHRoaXMgc3R1ZmYuDQoNClN0ZXZlDQoNCg== |
From: Goyal, G. (G. 097903) <L0...@ex...> - 2001-09-20 21:58:27
|
Steve, Thanks for the response.=20 I'm planning to implement the capability of holding multiple resultsets within a statement. Its actually a crucial functionality = that we'll need to put in, before we go all out on using mock objects.=20 Another thing that I'm planning to do is to make the MockResultSet object serializable and then to implement some kind of a simple GUI, to create these MockResultSet objects. This I think will save time while actually writing test cases. Its a bit painful to type in the code to instantiate the MockConnection,MockStatement, MockResultSet and the MockResultSetMetaData in every test. What do you think about this? Gaurav -----Original Message----- From: Steve Freeman [mailto:st...@m3...] Sent: Thursday, September 20, 2001 5:00 PM To: moc...@li... Subject: Re: [Mockobjects-java-dev] Multiple ResultSet problem Multiple ResultSet problemWe've just posted a fix that supports = multiple prepared statements -- it's in the CVS repository. If you're using a plain Statement, do you really need 2 instances, = shouldn't you use the same one twice? (just asking). Our mock statements probably do need support for multiple result sets. Please feel free to contribute a fix ;-) Otherwise, I should get around = to it over the next couple of weeks (got some other things to work on). Steve ----- Original Message -----=20 From: Goyal, Gaurav (GEA, 097903)=20 To: 'moc...@li...'=20 Sent: Thursday, September 20, 2001 9:30 PM Subject: [Mockobjects-java-dev] Multiple ResultSet problem Hi,=20 Been working on implementing mock objects in our project. Was = able to use MockConnection and MockStatement, but needed to make minor modifications on MockResultSet. The problem that I am facing now is this. One of the methods executes two queries. Both of the queries have different types of = result sets(different column counts). So when I pass my database connection to = this method, I should be able to create two statements out of my connection = which will return two different result sets. As of now MockConnection allows = me to set only one statment. So should I change/extend MockConnection in some = way so that it supports multiple statements? Has anybody faced this kind of problem? If yes, what solution did you go for?? Thanks,=20 Gaurav=20 2?$=A1=B8=DEr=DB#j=F6=9Dz=F9sSX=A7'X=AC=B4=CA=1C'?=E3y=CBl=8D=AB=DAu=EB=E5= S=CBl=B2<=ABq=E7=E8=AE=07=A7z=D8m=B6>?=FEX=AC=B6=CB(=BA=B7=1E~S=E0zw=AD=FE= X=AC=B6=CF=E5S =CBb=9D=FA?s?$=A1=B8=DEr=DB#j=F6=9D |
From: Steve F. <sm...@us...> - 2001-09-20 21:32:59
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv2983 Modified Files: papers.xml Log Message: Corrected sentence in introduction Index: papers.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers.xml,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- papers.xml 2001/09/09 00:48:55 1.2 +++ papers.xml 2001/09/20 21:32:57 1.3 @@ -19,7 +19,7 @@ <link href="papers/jdbc_testfirst.html">Developing JDBC applications test-first</link> <p> Many developers find unit testing software with third-party components, such - as databases, hard to do. It also shows how an approach based on + as databases, hard to do. This paper shows how an approach based on 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> |
From: Steve F. <st...@m3...> - 2001-09-20 21:10:33
|
TXVsdGlwbGUgUmVzdWx0U2V0IHByb2JsZW1XZSd2ZSBqdXN0IHBvc3RlZCBhIGZpeCB0aGF0IHN1 cHBvcnRzIG11bHRpcGxlIHByZXBhcmVkIHN0YXRlbWVudHMgLS0gaXQncyBpbiB0aGUgQ1ZTIHJl cG9zaXRvcnkuDQoNCklmIHlvdSdyZSB1c2luZyBhIHBsYWluIFN0YXRlbWVudCwgZG8geW91IHJl YWxseSBuZWVkIDIgaW5zdGFuY2VzLCBzaG91bGRuJ3QgeW91IHVzZSB0aGUgc2FtZSBvbmUgdHdp Y2U/IChqdXN0IGFza2luZykuDQoNCk91ciBtb2NrIHN0YXRlbWVudHMgcHJvYmFibHkgZG8gbmVl ZCBzdXBwb3J0IGZvciBtdWx0aXBsZSByZXN1bHQgc2V0cy4gUGxlYXNlIGZlZWwgZnJlZSB0byBj b250cmlidXRlIGEgZml4IDstKSBPdGhlcndpc2UsIEkgc2hvdWxkIGdldCBhcm91bmQgdG8gaXQg b3ZlciB0aGUgbmV4dCBjb3VwbGUgb2Ygd2Vla3MgKGdvdCBzb21lIG90aGVyIHRoaW5ncyB0byB3 b3JrIG9uKS4NCg0KU3RldmUNCg0KLS0tLS0gT3JpZ2luYWwgTWVzc2FnZSAtLS0tLSANCkZyb206 IEdveWFsLCBHYXVyYXYgKEdFQSwgMDk3OTAzKSANClRvOiAnbW9ja29iamVjdHMtamF2YS1kZXZA bGlzdHMuc291cmNlZm9yZ2UubmV0JyANClNlbnQ6IFRodXJzZGF5LCBTZXB0ZW1iZXIgMjAsIDIw MDEgOTozMCBQTQ0KU3ViamVjdDogW01vY2tvYmplY3RzLWphdmEtZGV2XSBNdWx0aXBsZSBSZXN1 bHRTZXQgcHJvYmxlbQ0KDQpIaSwgDQogICAgICAgIEJlZW4gd29ya2luZyBvbiBpbXBsZW1lbnRp bmcgbW9jayBvYmplY3RzIGluIG91ciBwcm9qZWN0LiBXYXMgYWJsZSB0byB1c2UgTW9ja0Nvbm5l Y3Rpb24gYW5kIE1vY2tTdGF0ZW1lbnQsIGJ1dCBuZWVkZWQgdG8gbWFrZSBtaW5vciBtb2RpZmlj YXRpb25zIG9uIE1vY2tSZXN1bHRTZXQuDQogICAgICAgIFRoZSBwcm9ibGVtIHRoYXQgSSBhbSBm YWNpbmcgbm93IGlzIHRoaXMuIE9uZSBvZiB0aGUgbWV0aG9kcyBleGVjdXRlcyB0d28gcXVlcmll cy4gQm90aCBvZiB0aGUgcXVlcmllcyBoYXZlIGRpZmZlcmVudCB0eXBlcyBvZiByZXN1bHQgc2V0 cyhkaWZmZXJlbnQgY29sdW1uIGNvdW50cykuIFNvIHdoZW4gSSBwYXNzIG15IGRhdGFiYXNlIGNv bm5lY3Rpb24gdG8gdGhpcyBtZXRob2QsIEkgc2hvdWxkIGJlIGFibGUgdG8gY3JlYXRlIHR3byBz dGF0ZW1lbnRzIG91dCBvZiBteSBjb25uZWN0aW9uIHdoaWNoIHdpbGwgcmV0dXJuIHR3byBkaWZm ZXJlbnQgcmVzdWx0IHNldHMuIEFzIG9mIG5vdyBNb2NrQ29ubmVjdGlvbiBhbGxvd3MgbWUgdG8g c2V0IG9ubHkgb25lIHN0YXRtZW50LiBTbyBzaG91bGQgSSBjaGFuZ2UvZXh0ZW5kIE1vY2tDb25u ZWN0aW9uIGluIHNvbWUgd2F5IHNvIHRoYXQgaXQgc3VwcG9ydHMgbXVsdGlwbGUgc3RhdGVtZW50 cz8gSGFzIGFueWJvZHkgZmFjZWQgdGhpcyBraW5kIG9mIHByb2JsZW0/IElmIHllcywgd2hhdCBz b2x1dGlvbiBkaWQgeW91IGdvIGZvcj8/DQpUaGFua3MsIA0KR2F1cmF2IA0K |
From: Goyal, G. (G. 097903) <L0...@ex...> - 2001-09-20 20:31:16
|
Hi, Been working on implementing mock objects in our project. Was able to use MockConnection and MockStatement, but needed to make minor modifications on MockResultSet. The problem that I am facing now is this. One of the methods executes two queries. Both of the queries have different types of result sets(different column counts). So when I pass my database connection to this method, I should be able to create two statements out of my connection which will return two different result sets. As of now MockConnection allows me to set only one statment. So should I change/extend MockConnection in some way so that it supports multiple statements? Has anybody faced this kind of problem? If yes, what solution did you go for?? Thanks, Gaurav |
From: Vincent M. <vm...@oc...> - 2001-09-20 10:57:37
|
Hi Frank, As Steve mentionned, I would very interested to benefit from your experience in refactoring for the Cactus project ! (I can tell you there is some work there ... :) ). I would very gladly incorporate the refactorings you will do into the project. The home page is http://jakarta.apache.org/cactus. Tell me if you would be interested. Thanks a lot -Vincent Massol ----- Original Message ----- From: "Steve Freeman" <st...@m3...> To: <moc...@li...> Sent: Wednesday, September 19, 2001 9:28 PM Subject: Re: [Mockobjects-java-dev] Refactoring mockobjects > > The HamburgXpUsersGroup decided to go for > > a refactoring episode on our next meeting > > on October 15. I suggested mockobjects. > > > > Before y'all fly out to Tampa, what needs > > doing right now, can you pin that down to > > a short list of refactoring targets? > > I'm not sure that there's anything in the library that really needs refactoring. We spent quite some time on the expectations and the mocks don't have that much. The only thing that really needs doing is some tidying up of some of the names, but that's not very interesting. > > You could drop a line to Vincent Massol and see if there's anything in Cactus. > > Steve > > 2Ąrjz´yu˛qŽzśţśş~zţśĄrj |
From: Steve F. <sm...@us...> - 2001-09-19 21:38:34
|
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/sql In directory usw-pr-cvs1:/tmp/cvs-serv11269 Modified Files: MockConnection.java Log Message: Added multiple prepared statements Added autoCommit Index: MockConnection.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/sql/MockConnection.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- MockConnection.java 2001/08/11 20:42:05 1.3 +++ MockConnection.java 2001/09/19 21:38:31 1.4 @@ -1,21 +1,20 @@ package com.mockobjects.sql; import java.sql.*; -import java.util.Map; -import com.mockobjects.ExpectationCounter; -import com.mockobjects.ExpectationValue; -import com.mockobjects.MockObject; +import java.util.*; +import com.mockobjects.*; public class MockConnection extends MockObject implements Connection { private ExpectationCounter myCommitCalls = new ExpectationCounter("MockConnection.commit"); private ExpectationCounter myRollbackCalls = new ExpectationCounter("MockConnection.rollback"); private ExpectationCounter myCloseCalls = new ExpectationCounter("MockConnection.close"); - private ExpectationValue myPrepareStatementString = new ExpectationValue("MockConnection.preparedStatementString"); - private PreparedStatement myPreparedStatement; + private ExpectationCollection myPreparedStatementStrings = new ExpectationList("MockConnection.preparedStatementString"); + private ArrayList myPreparedStatements = new ArrayList(); private SQLException myStatementException = null; private ExpectationCounter myCreateStatementCalls = new ExpectationCounter("MockConnection.createStatement"); + private ExpectationValue myAutoCommit = new ExpectationValue("MockConnection.setAutoCommit"); private Statement myStatement; public MockConnection() { @@ -34,16 +33,16 @@ myCreateStatementCalls.setExpected(calls); } - public void setExpectedPrepareStatementString(String sql) { - myPrepareStatementString.setExpected(sql); + public void addExpectedPreparedStatementString(String sql) { + myPreparedStatementStrings.addExpected(sql); } public void setExpectedRollbackCalls(int callCount) { myRollbackCalls.setExpected(callCount); } - public void setupPreparedStatement(PreparedStatement prepared) { - myPreparedStatement = prepared; + public void setupAddPreparedStatement(PreparedStatement prepared) { + myPreparedStatements.add(prepared); } public void setupStatement(Statement statement) { @@ -129,9 +128,9 @@ } public PreparedStatement prepareStatement(String sql) throws SQLException { - myPrepareStatementString.setActual(sql); + myPreparedStatementStrings.addActual(sql); throwStatementExceptionIfAny(); - return myPreparedStatement; + return (PreparedStatement) myPreparedStatements.remove(0); } public PreparedStatement prepareStatement( @@ -146,9 +145,12 @@ myRollbackCalls.inc(); } - public void setAutoCommit(boolean autoCommit) - throws SQLException { - throw new UnsupportedOperationException(); + public void setExpectedAutoCommit(boolean autoCommit) { + myAutoCommit.setExpected(autoCommit); + } + + public void setAutoCommit(boolean autoCommit) throws SQLException { + myAutoCommit.setActual(autoCommit); } public void setCatalog(String catalog) throws SQLException { |
From: Steve F. <st...@m3...> - 2001-09-19 20:46:26
|
PiBUaGUgSGFtYnVyZ1hwVXNlcnNHcm91cCBkZWNpZGVkIHRvIGdvIGZvcg0KPiBhIHJlZmFjdG9y aW5nIGVwaXNvZGUgb24gb3VyIG5leHQgbWVldGluZw0KPiBvbiBPY3RvYmVyIDE1LiAgSSBzdWdn ZXN0ZWQgbW9ja29iamVjdHMuDQo+IA0KPiBCZWZvcmUgeSdhbGwgZmx5IG91dCB0byBUYW1wYSwg d2hhdCBuZWVkcw0KPiBkb2luZyByaWdodCBub3csIGNhbiB5b3UgcGluIHRoYXQgZG93biB0bw0K PiBhIHNob3J0IGxpc3Qgb2YgcmVmYWN0b3JpbmcgdGFyZ2V0cz8NCg0KSSdtIG5vdCBzdXJlIHRo YXQgdGhlcmUncyBhbnl0aGluZyBpbiB0aGUgbGlicmFyeSB0aGF0IHJlYWxseSBuZWVkcyByZWZh Y3RvcmluZy4gV2Ugc3BlbnQgcXVpdGUgc29tZSB0aW1lIG9uIHRoZSBleHBlY3RhdGlvbnMgYW5k IHRoZSBtb2NrcyBkb24ndCBoYXZlIHRoYXQgbXVjaC4gVGhlIG9ubHkgdGhpbmcgdGhhdCByZWFs bHkgbmVlZHMgZG9pbmcgaXMgc29tZSB0aWR5aW5nIHVwIG9mIHNvbWUgb2YgdGhlIG5hbWVzLCBi dXQgdGhhdCdzIG5vdCB2ZXJ5IGludGVyZXN0aW5nLiANCg0KWW91IGNvdWxkIGRyb3AgYSBsaW5l IHRvIFZpbmNlbnQgTWFzc29sIGFuZCBzZWUgaWYgdGhlcmUncyBhbnl0aGluZyBpbiBDYWN0dXMu DQoNClN0ZXZlDQoNCg== |
From: Frank W. <wes...@ac...> - 2001-09-18 20:41:07
|
The HamburgXpUsersGroup decided to go for a refactoring episode on our next meeting on October 15. I suggested mockobjects. Before y'all fly out to Tampa, what needs doing right now, can you pin that down to a short list of refactoring targets? Frank |
From: Jeff M. <je...@mk...> - 2001-09-17 17:28:58
|
Here's a patch for MockConnection to support multiple PreparedStatements and the setAutoCommit method. Hope it's of use -- Jeff Martin Information Technologist http://www.mkodo.com/ |
From: Steve F. <st...@m3...> - 2001-09-16 17:01:27
|
DQotLS0tLSBPcmlnaW5hbCBNZXNzYWdlIC0tLS0tIA0KRnJvbTogIlN0ZXZlIEZyZWVtYW4iIDxz dGV2ZUBtM3AuY28udWs+DQpUbzogIkhlbnJpayBLYXJsc3NvbiIgPEhlbnJpay5LYXJsc3NvbkBp Y29ubWVkaWFsYWIuc2U+DQpTZW50OiBTdW5kYXksIFNlcHRlbWJlciAxNiwgMjAwMSA1OjU0IFBN DQpTdWJqZWN0OiBSZTogW01vY2tvYmplY3RzLWphdmEtZGV2XSBSZTogTGlzdHMgb2YgdmFsdWVz IHRvIHJldHVybg0KDQoNCj4gRnJvbTogIkhlbnJpayBLYXJsc3NvbiIgPEhlbnJpay5LYXJsc3Nv bkBpY29ubWVkaWFsYWIuc2U+DQo+ID4gVGhpcyB0ZXN0IHdhcyBtYWRlIGZvciBhIGNsYXNzIHRo YXQgbmF2aWdhdGVzIGluIGEgYmFjayBlbmQgc3lzdGVtIG1lbnUNCj4gPiBzY3JlZW5zLiBXaGVu IHN0YXJ0aW5nIGluIGFueSBtZW51LCBJIHdhbnQgdG8gcmVhY2ggbWFpbiBtZW51IGJ5IGNhbGxp bmcNCj4gPiBuYXZpZ2F0ZVRvTWFpbi4gbmF2aWdhdGVUb01haW4gd2lsbCBpdGVyYXRlIHRocm91 Z2ggdmFyaW91cyBzY3JlZW5zIGFuZA0KPiA+IG1lbnVlcy4gDQo+ID4gDQo+ID4gICBwdWJsaWMg dm9pZCB0ZXN0TmF2aWdhdGVUb01haW4oKSANCj4gPiAgICAgLy9tYWtlIGl0IGxvb2sgbGlrZSB3 ZSBhcmUgaW4gYSBzdWIgbWVudQ0KPiA+ICAgICAuLi4NCj4gPiAgICAgbmF2aWdhdG9yLnNldFNj cmVlbihteU1vY2tTY3JlZW4pOw0KPiA+ICAgICAuLi4NCj4gPiAgICAgbXlNb2NrU2NyZWVuLmFj dHVhbEdldFN0cmluZ1JldHVyblZhbHVlcy5hZGRSZXR1cm4oIlNVQl9NRU5VXzEuMS4xIik7DQo+ ID4gICAgIG15TW9ja1NjcmVlbi5hY3R1YWxHZXRTdHJpbmdSZXR1cm5WYWx1ZXMuYWRkUmV0dXJu KCJTVUJfTUVOVV8xLjEiKTsNCj4gPiAgICAgbXlNb2NrU2NyZWVuLmFjdHVhbEdldFN0cmluZ1Jl dHVyblZhbHVlcy5hZGRSZXR1cm4oIlNVQl9NRU5VXzEiKTsgICANCj4gPiAgICAgbXlNb2NrU2Ny ZWVuLmFjdHVhbEdldFN0cmluZ1JldHVyblZhbHVlcy5hZGRSZXR1cm4oIk1BSU4iKTsgICAgDQo+ ID4gICAgIC8vY2hlY2sgdGhhdCB3ZSBjYW4gZ28gdG8gbWFpbiBtZW51DQo+ID4gICAgIGFzc2Vy dFRydWUobmF2aWdhdG9yLm5hdmlnYXRlVG9NYWluKCkpOw0KPiA+ICAgfQ0KPiANCj4gSSBkb24n dCB1bmRlcnN0YW5kIGhvdyB5b3VyIHJldHVybiB2YWx1ZXMgb2JqZWN0IGRpZmZlcnMgZnJvbSBv bmUgb2YgdGhlIHN0YW5kYXJkIGphdmEgY29sbGVjdGlvbnMuIEZvciBleGFtcGxlLCB5b3UgY291 bGQgZ2V0IHNpbWlsYXIgYmVoYXZpb3VyIGJ5IHVzaW5nIGFuIEFycmF5TGlzdCBhbmQgcmVtb3Zp bmcgdGhlIGZpcnN0IGVsZW1lbnQuIFRoZSBvbmx5IG1vY2sgYXNwZWN0IG9mIHRoZSBtb2NrIHNj cmVlbiBpcyB0aGUgY291bnQgb2YgdGhlIG51bWJlciBvZiBjYWxscyB0byBnZXRTY3JlZW5JZCgp LiBJIHdvdWxkIGd1ZXNzIHRoYXQgdGhlIHRlc3QgY291bGQgYmUgcmV3cml0dGVuOg0KPiANCj4g ICBwdWJsaWMgdm9pZCB0ZXN0TmF2aWdhdGVUb01haW4oKSB7DQo+ICAgICAvLyBzdGFydCBpbiBh IHN1Yi1tZW51DQo+ICAgICBuYXZpZ2F0b3Iuc2V0U2NyZWVuKG15TW9ja1NjcmVlbik7DQo+ICAg ICBteU1vY2tTY3JlZW4uc2V0U2NyZWVuSURzKFNDUkVFTl9JRFMpOw0KPiANCj4gICAgIG15TW9j a1NjcmVlbi5zZXRFeHBlY3RlZEdldFNjcmVlbklkQ2FsbHMoU0NSRUVOX0lEUy5sZW5ndGgpOw0K PiANCj4gICAgIGFzc2VydFRydWUoIlNob3VsZCBuYXZpZ2F0ZSB0byBtYWluIiwgbmF2aWdhdG9y Lm5hdmlnYXRlVG9NYWluKCkpOw0KPiAgICAgbXlNb2NrU2NyZWVuLnZlcmlmeSgpOw0KPiAgIH0N Cj4gDQo+IFRoZSBwb2ludCBhYm91dCB0aGlzIGNoYW5nZSBpcyB0aGF0IGl0IG1ha2VzIGV4cGxp Y2l0IHdoaWNoIHBhcnQgb2YgdGhlIHRlc3QgaXMgc2V0dXAgYW5kIHdoaWNoIGlzIGFzc2VydGlv biAoaS5lLiBleHBlY3RhdGlvbiksIGFuZCBpdCBjYWxscyB0aGUgdmVyaWZ5KCkgdG8gbWFrZSBj b25maXJtIHRoZSBleHBlY3RhdGlvbi4NCj4gDQo+IEknZCBoYXZlIHRvIHNlZSB0aGUgY29kZSwg YnV0IG15IGd1ZXNzIGlzIHRoYXQgeW91IG5lZWQgdG8gdGVzdCB0aGUgY29uZGl0aW9uczoNCj4g LSB0aGUgc2NyZWVuIGlzIGFscmVhZHkgYXQgbWFpbg0KPiAtIHRoZSBzY3JlZW4gaXMgb25lIGxl dmVsIGRlZXANCj4gLSB0aGUgc2NyZWVuIGlzIG1vcmUgdGhhbiBvbmUgbGV2ZWwgZGVlcA0KPiAN Cj4gQXMgZm9yIHRoZSBleGNlcHRpb24gdmVyc2lvbiwgbXkgZ3Vlc3MgaXMgdGhhdCB5b3VyIGNv ZGUgaXMgYSBsb29wIG9mIHNvbWUgc29ydCBhbmQgaXQgZG9lc24ndCByZWFsbHkgbWF0dGVyIHdo ZXJlIHRoZSBleGNlcHRpb24gaXMgdGhyb3duIHRvIHZhbGlkYXRlIHRoZSBsb2dpYyAocGxlYXNl IGNvcnJlY3QgbWUpLiBUaGlzIHdvdWxkIG1ha2UgdGhlIHNlY29uZCB0ZXN0Og0KPiANCj4gICBw dWJsaWMgdm9pZCB0ZXN0VHJhbnNGYWlsdXJlKCkgew0KPiAgICAgLy8gc3RhcnQgaW4gYSBzdWIt bWVudQ0KPiAgICAgbmF2aWdhdG9yLnNldFNjcmVlbihteU1vY2tTY3JlZW4pOw0KPiAgICAgbXlN b2NrU2NyZWVuLnNldHVwVGhyb3dFeGNlcHRpb25PbkdldFNjcmVlbklkKFNDUkVFTl9FWENFUFRJ T04pOw0KPiANCj4gICAgIG15TW9ja1NjcmVlbi5zZXRFeHBlY3RlZEdldFNjcmVlbklkQ2FsbHMo MSk7IC8vIHRoaXMgc2hvdWxkIHN0aWxsIGJlIGNhbGxlZCBvbmNlDQo+IA0KPiAgICAgYXNzZXJ0 VHJ1ZSgiU2hvdWxkIG5vdCBuYXZpZ2F0ZSB0byBtYWluIiwgISBuYXZpZ2F0b3IubmF2aWdhdGVU b01haW4oKSk7DQo+ICAgICBteU1vY2tTY3JlZW4udmVyaWZ5KCk7ICAgICAgICANCj4gICB9DQo+ IA0KPiBXaGF0IGRvIHlvdSB0aGluaz8NCj4gDQo+IFN0ZXZlDQo+IA0KPiANCg== |
From: Steve F. <sm...@us...> - 2001-09-16 16:55:10
|
Update of /cvsroot/mockobjects/doc In directory usw-pr-cvs1:/tmp/cvs-serv21354 Removed Files: jdbc_testfirst.html Log Message: moved to website --- jdbc_testfirst.html DELETED --- |
From: Henrik K. <Hen...@ic...> - 2001-09-13 07:06:09
|
> We tend to separate the two roles - checking expectations and stubbing > values - with the one exception of the ExpectationMap. Could you post > a couple of example tests to help me understand how you're thinking? Here are some tests together with a prototype for ReturnLists. This test was made for a class that navigates in a back end system menu screens. When starting in any menu, I want to reach main menu by calling navigateToMain. navigateToMain will iterate through various screens and menues. public void testNavigateToMain() //make it look like we are in a sub menu ... navigator.setScreen(myMockScreen); ... myMockScreen.actualGetStringReturnValues.addReturn("SUB_MENU_1.1.1"); myMockScreen.actualGetStringReturnValues.addReturn("SUB_MENU_1.1"); myMockScreen.actualGetStringReturnValues.addReturn("SUB_MENU_1"); myMockScreen.actualGetStringReturnValues.addReturn("MAIN"); //check that we can go to main menu assertTrue(navigator.navigateToMain()); } Next example: test that errors are handled when back end aborts transaction. public void testNavigateToMain() myMockScreen.actualGetStringReturnValues.addReturn("SUB_MENU_1.1.1"); myMockScreen.actualGetStringReturnValues.addReturn("SUB_MENU_1.1"); myMockScreen.actualGetStringReturnValues.addReturn("SUB_MENU_1"); myMockScreen.actualGetStringReturnValues.addReturn(new TransAbortedException()); //check the error handling assertTrue(!navigator.navigateToMain()); } -------- Some source, first my current implementation of return lists classes. On this level the roles are separated, but lists could hold exceptions or values as needed. ---------------------- ReturnCollection ----------------------- package com.mockobjects; import java.util.Enumeration; import java.util.Iterator; /** * Holder of values and objects to return from mock object methods. */ public interface ReturnCollection { /** * get next return value in the sequence */ public Object getNextReturn(); /** * Look at next value in the sequence */ public Object peekNextReturn(); /** * Checks if there are more items to return. */ public boolean hasMoreToReturn(); /** * Adds items. */ public void addReturn(Object ReturnItem); public void addReturn(boolean ReturnItem); public void addReturn(long ReturnItem); public void addReturn(double ReturnItem); public void addReturnMany(Object[] items); public void addReturnMany(Object item, int noOfItems); public void addReturnMany(Enumeration items); public void addReturnMany(Iterator items); /** * Delete all return values. */ public void returnNothing(); } ---------------------- AbstractReturnCollection ------------------- package com.mockobjects; import java.util.Enumeration; import java.util.Iterator; import java.util.Collection; /** * Base implementation of the ReturnCollection interface. * @see ReturnCollection */ public abstract class AbstractReturnCollection implements ReturnCollection{ protected String myName; /** * Constructor that names the instance, currently the name is not used */ public AbstractReturnCollection(String name) { myName = name; } public void addReturn(Object ReturnItem) { getReturnCollection().add(ReturnItem); } public void addReturn(boolean ReturnItem) { getReturnCollection().add(new Boolean(ReturnItem)); } public void addReturn(long ReturnItem) { getReturnCollection().add(new Long(ReturnItem)); } public void addReturn(double ReturnItem) { getReturnCollection().add(new Double(ReturnItem)); } public void addReturnMany(Object[] items) { for (int i = 0; i < items.length; ++i) { addReturn(items[i]); } } public void addReturnMany(Object item, int noOfItems) { for (int i = 0; i < noOfItems; ++i) { addReturn(item); } } public void addReturnMany(Enumeration items) { while (items.hasMoreElements()) { addReturn(items.nextElement()); } } public void addReturnMany(Iterator items) { while (items.hasNext()) { addReturn(items.next()); } } /** * Delete all return values. */ public void returnNothing() { getReturnCollection().clear(); } /** * get the collection of stored return values */ protected abstract Collection getReturnCollection(); /** * Gets next value. */ public abstract Object getNextReturn(); /** * Checks if more values exist. */ public abstract boolean hasMoreToReturn(); } ---------------------- ReturnList ------------------- package com.mockobjects; import java.util.Collection; import java.util.ArrayList; /** * Holds a collection of return values in a list. */ public class ReturnList extends AbstractReturnCollection { protected ArrayList myReturnItems; protected int nextReturn = 0; public ReturnList(String name) { super(name); myReturnItems = new ArrayList(); } protected Collection getReturnCollection() { return myReturnItems; } /** * Get the next return value */ public Object getNextReturn() { return ((ArrayList)getReturnCollection()).get(nextReturn++); } public Object peekNextReturn(){ return ((ArrayList)getReturnCollection()).get(nextReturn); } public boolean hasMoreToReturn(){ return nextReturn <= getReturnCollection().size()-1; } } --------------------------------------- Finally, some code from a mock object. This code was generated automatically by a MockMaker extension. At this level the throwing of exceptions and returning of values are mixed public int getScreenID() throws CSSTransStoppedException, CSSAbendException{ myGetScreenIDCalls.inc(); if (actualGetScreenIDReturnValues.hasMoreToReturn()){ if(actualGetScreenIDReturnValues.peekNextReturn() instanceof CSSTransStoppedException){ throw (CSSTransStoppedException)actualGetScreenIDReturnValues.getNextReturn(); } if(actualGetScreenIDReturnValues.peekNextReturn() instanceof CSSAbendException){ throw (CSSAbendException)actualGetScreenIDReturnValues.getNextReturn(); } return ((Integer)actualGetScreenIDReturnValues.getNextReturn()).intValue(); } else { return myActualGetScreenIDReturnValue; } } /Henrik |
From: Henrik K. <Hen...@ic...> - 2001-09-11 11:12:12
|
Hi! Vincent Massol asked med to repost this thread to the list. My original request is at the end of this mail. Some more comments about the suggested functions: Actually, I frequently use the concept of expectations, for instance when generating mock objects with MockMaker. I also used those classes as pattern when developing mine. If i don't get it completely wrong I should use expectations when I want to test that my mock objects' methods are called with correct and expected parameters. What I would like to see (I havn't been able to use Expectations for this) is a way to make lists of values to return rather than values to expect. Below is a very simple example of ho I currently use my ReturnCollection. public String getString(int arg0, int arg1, int arg2){ if (actualGetStringReturnValues.hasMoreToReturn()){ return (String)actualGetStringReturnValues.getNextReturn(); } } Another example, this time I use ReturnCollection for both values to return and exceptions to throw. (This code was auto generated with an extension to MockMaker I'm currently trying to implement) public int getScreenID() throws CSSTransStoppedException, CSSAbendException{ myGetScreenIDCalls.inc(); if (actualGetScreenIDReturnValues.hasMoreToReturn()){ if(actualGetScreenIDReturnValues.peekNextReturn() instanceof CSSTransStoppedException){ throw (CSSTransStoppedException)actualGetScreenIDReturnValues.getNextReturn(); } if(actualGetScreenIDReturnValues.peekNextReturn() instanceof CSSAbendException){ throw (CSSAbendException)actualGetScreenIDReturnValues.getNextReturn(); } return ((Integer)actualGetScreenIDReturnValues.getNextReturn()).intValue(); } else { return myActualGetScreenIDReturnValue; } } /Henrik Karlsson -----Original Message----- From: Vincent Massol To: Henrik Karlsson Sent: 2001-09-11 11:24 Subject: Re: New classes, examples attached Thanks Henrik ! I just want to get it straight. Are you referring to the Mock Objects project or to the Cactus one ? :) It looks more likely it is the Mock Objects project to me ... I think what you are referring to already exist in the framework : it is called lists. Can you repost your email to moc...@li... (that's supposing you're referring to the Mock Objects project). However, we are always in need of help (there is so many things to do) and would be very glad for your help. Tell us if there is something that you'd like to do (we should also update the todo list). Thanks -Vincent ----- Original Message ----- From: "Henrik Karlsson" <hen...@ic...> To: <vm...@oc...> Sent: Tuesday, September 11, 2001 8:00 AM Subject: New classes, examples attached > Hi! > After using mock objects for a couple of months I sometimes > need objects that can return secuences of responses, like > reading sequences of strings from a back end system. > If you find these ideas useful I'll happily take part in > developing them for future releases. I have source code > that implements some working prototypes, see example below. > > I've added a couple of classes: > public interface ReturnCollection { > public Object getNextReturn(); > public Object peekNextReturn(); > public boolean hasMoreToReturn(); > public void addReturn(Object ReturnItem); > public void addReturn ... > ... > > public abstract class AbstractReturnCollection implements > ReturnCollection ... > > public class ReturnList extends AbstractReturnCollection ... > > ReturnList is the first concrete class, other special > purpose children of AbstractReturnCollection could be > RandomReturnSet, CircularReturnSet ... > > /Henrik Karlsson > |
From: Steve F. <sm...@us...> - 2001-09-09 00:50:00
|
Update of /cvsroot/mockobjects/mockobjects-java In directory usw-pr-cvs1:/tmp/cvs-serv13284 Modified Files: build.xml Log Message: Copy papers directory to website Index: build.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/build.xml,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- build.xml 2001/08/21 11:11:59 1.13 +++ build.xml 2001/09/09 00:49:57 1.14 @@ -454,6 +454,11 @@ <fileset dir="${xdoc.dir}/images"/> </copy> + <!-- Copy the papers --> + <copy todir="${out.site.dir}/papers"> + <fileset dir="${xdoc.dir}/papers"/> + </copy> + <!-- Copy other files (non XML) --> <copy todir="${out.site.dir}"> <fileset dir="${xdoc.dir}"> |
From: Steve F. <sm...@us...> - 2001-09-09 00:48:59
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv13129 Modified Files: index.xml papers.xml Log Message: Fixed internal link and page title. Index: index.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/index.xml,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- index.xml 2001/09/08 16:32:00 1.14 +++ index.xml 2001/09/09 00:48:54 1.15 @@ -48,8 +48,7 @@ </li> </ul> <p> - Our larger goal is to make - <link href="www.mockobjects.com">www.mockobjects.com</link> + Our larger goal is to make this site <em>the</em> point of reference for ideas and tools for unit testing particularly based on Mock Objects. Our first implementation is in Java, largely because that's what we've been working in, but also Index: papers.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers.xml,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- papers.xml 2001/09/08 16:32:00 1.1 +++ papers.xml 2001/09/09 00:48:55 1.2 @@ -4,7 +4,7 @@ <document> <header> - <title>Downloads</title> + <title>Papers</title> <authors> <person name="Steve Freeman" email="sm...@us..."/> </authors> |
From: Steve F. <sm...@us...> - 2001-09-08 16:32:03
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv14648/xdocs Modified Files: site-book.xml index.xml doc-book.xml Added Files: papers.xml Log Message: added papers section --- NEW FILE: papers.xml --- <?xml version="1.0"?> <!DOCTYPE document SYSTEM "./dtd/document-v10.dtd"> <document> <header> <title>Downloads</title> <authors> <person name="Steve Freeman" email="sm...@us..."/> </authors> </header> <body> <s1 title="Papers"> <p>This is a collection of papers on Mock Objects style and practice. </p> <sl> <li> <link href="papers/jdbc_testfirst.html">Developing JDBC applications test-first</link> <p> Many developers find unit testing software with third-party components, such as databases, hard to do. It also shows how an approach based on 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> </sl> </s1> </body> </document> Index: site-book.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/site-book.xml,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- site-book.xml 2001/08/21 11:41:27 1.8 +++ site-book.xml 2001/09/08 16:32:00 1.9 @@ -17,7 +17,8 @@ </menu> <menu label="Documentation"> - <menu-item label="What is it ?" source="endotesting.xml"/> + <menu-item label="Introduction" source="endotesting.xml"/> + <menu-item label="Papers" source="papers.xml" /> <menu-item label="Naming conventions" source="naming_conventions.xml"/> <menu-item type="external" label="API Reference" href="javadoc/index.html"/> </menu> Index: index.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/index.xml,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- index.xml 2001/08/27 02:43:33 1.13 +++ index.xml 2001/09/08 16:32:00 1.14 @@ -63,6 +63,10 @@ <table> <tr> + <td>2001/09/08</td> + <td>Added a new section for papers on Mock Objects</td> + </tr> + <tr> <td>2001/08/06</td> <td> The Mock Objects project now has a user web site. Index: doc-book.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/doc-book.xml,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- doc-book.xml 2001/08/27 02:43:33 1.9 +++ doc-book.xml 2001/09/08 16:32:00 1.10 @@ -17,7 +17,8 @@ </menu> <menu label="Documentation"> - <menu-item label="What are Mock Objects?" source="endotesting.xml"/> + <menu-item label="Introduction" source="endotesting.xml"/> + <menu-item label="Papers" source="papers.xml" /> <menu-item label="Naming conventions" source="naming_conventions.xml"/> <menu-item type="external" label="API Reference" href="javadoc/index.html"/> </menu> |
From: Steve F. <sm...@us...> - 2001-09-08 16:30:42
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv14201 Added Files: jdbc_testfirst.html Log Message: added jdbc paper --- NEW FILE: jdbc_testfirst.html --- <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} 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} li { font-family: Arial, Helvetica, sans-serif } h2 { font-family: Arial, Helvetica, sans-serif; margin-top: 3%} --> </style> </head> <body bgcolor="#FFFFFF"> <h1>Developing JDBC applications test-first</h1> <p align="center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> <p>Many developers find unit testing software with third-party components, such as databases, hard to do. Usually the difficulties are because setting up the component to ensure no dependencies between unit tests is too complicated or slow. This paper uses JDBC, the Java database interface, to show how it is possible to avoid using a real database at all. It also shows how an approach based on Mock Objects can lead to more rigourous unit testing and, I believe, better structured code than working with a real database. </p> <p>There are two key skills for writing Mock Objects when developing code test-first. First, decide what a test would need to verify to show that it had passed, and add mock implementations of objects that represent those concepts. Second, do not attempt to reproduce real behaviour in a mock object; if they are becoming complex, revisit both the mock implementation and the code being tested to see if there is some intermediate concept to be factored out.</p> <h3>Note</h3> <p>All the code in these examples and the mock objects library are available for download from <a href="http://www.mockobjects.com">http://www.mockobjects.com</a>. If you are unfamiliar with Mock Objects, please read the introductory paper in the Documentation section. </p> <h3>The stories</h3> <p>To help structure the examples, let's imagine that our customers have commissioned us to write a mailing list system. Anyone can add and remove themselves from the list, but only the list owner can see the whole list and send messages to it. If we start by assuming that there's just one list, we have the following tasks:</p> <ul> <li>anyone can add their name and email address to the list;</li> <li>anyone who is a member of the list can remove themselves, using their email address;</li> <li>the owner of the list can see all the members of the list;</li> <li>the owner of the list can send a message to all the members.</li> </ul> <h2>Add a member to the list</h2> <p>We assume that access is controlled by the surrounding system and start to develop a <span class="inline_code">MailingList</span> class to manage the list. For a succesful insertion, we would expect certain things to happen:</p> <ul> <li>create a statement object with the appropriate insertion statement</li> <li>set the parameters for the insertion statement based on the member's email address and name</li> <li>execute the statement exactly once to make the insertion</li> <li>close the statement exactly once</li> </ul> <p>Let's write that into our first test.</p> <pre> public void testAddNewMember() { connection.setExpectedPrepareStatementString(MailingList.INSERTION_SQL); statement.addExpectedSetParameters(new Object[] {EMAIL, NAME}); statement.setExpectedExecuteCalls(1); statement.setExpectedCloseCalls(1); <span class="deemphasised">[...]</span> }</pre> <p>This tells us that we will need to check assertions about the connection and statement objects. It also pushes us towards passing the connection object through to the mailing list object when we make the call to add a member. If we rename our database objects to make it explicit that they are mock implementations, the test becomes:</p> <pre> public class TestMaililingList extends TestCaseMo { public void testAddNewMember() throws SQLException { <span class="deemphasised"> mockConnection.setExpectedPrepareStatementString(MailingList.INSERTION_SQL); mockStatement.addExpectedSetParameters(new Object[] {EMAIL, NAME}); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1); </span> mailingList.addMember(mockConnection, EMAIL, NAME); mockStatement.verify(); mockConnection.verify(); } <span class="deemphasised">[...]</span> } </pre> <p></p> <p>Note that <span class="inline_code">testAddNewMember() </span>declares <span class="inline_code">SQLException</span> in its throws clause. This means that any <span class="inline_code">SQLException</span> will be caught by the JUnit framework and reported as an error (a breakdown in our environment). Strictly speaking, we should catch it and convert it to a failure (a mistake in coding), but this style is lightweight and accurate enough for many projects. </p> <p>We set up the test in another method:</p> <pre> public void setUp() { private MailingList list = new MailingList(); private MockConnection mockConnection = new MockConnection(); private MockPreparedStatement mockStatement = new MockPreparedStatement(); mockConnection.setupPreparedStatement(mockStatement); }</pre> <p>The simplest implementation to pass this first test is just to work through each of the expectations that we've set:</p> <pre> public class MailingList { public void addMember(Connection connection, String emailAddress, String name) throws SQLException { PreparedStatement statement = connection.prepareStatement(INSERT_SQL); statement.setString(1, emailAddress); statement.setString(2, name); statement.execute(); statement.close(); } }</pre> <p>You may have noticed that this implementation does not ensure that a statement will always be closed; we'll get to that later.</p> <h3>What have we learned?</h3> <p>This first test is very localised. It tests exactly one thing, the successful addition of a list member, and has no external dependencies, such as the need for an installed database with its tables in the right state. In some situations, where infrastructure decisions have not been taken, this will allow a project to move ahead while the management are negotiating with your vendors.</p> <p>There are two aspects to testing database code: what we say to the database, and what the database expects to hear. This approach separates them, which makes it easier to pinpoint errors as the system grows. Of course, we will also need integration tests to check the whole process, but those should be more like an acceptance test for a larger module.</p> <p>This testing style pushes us towards passing objects around. In this case, we need access to the connection object to verify it, so the easiest thing is to pass it through to the mailing list along with the other parameters. In practice, many systems hold connections in pools to improve performance, so passing through a connection with each action is probably the right approach. We have found that this style of test-first programming pushes us towards the right design so often that we have come to rely on it.</p> <h2>Add an existing member to the list</h2> <p>What happens if someone tries to add an email address twice? After further discussion, our customers decide that we should not accept the change and show an error message; we decide to throw a <span class="inline_code">MailingListException</span> when this happens. We can write a test to verify that such an exception is thrown if there is a duplicate record.</p> <pre> public void testAddExistingMember() throws SQLException { mockStatement.setupThrowExceptionOnExecute( new SQLException("MockStatment", "Duplicate", DatabaseConstants.UNIQUE_CONSTRAINT_VIOLATED)); <span class="deemphasised"> mockConnection.setExpectedPrepareStatementString(MailingList.INSERTION_SQL); mockStatement.addExpectedSetParameters(new Object[] {EMAIL, NAME}); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1);</span> try { mailingList.addMember(mockConnection, EMAIL, NAME); fail("should have thrown an exception"); } catch (MailingListException expected) { } <span class="deemphasised"> mockStatement.verify(); mockConnection.verify(); }</span> </pre> <p>The method <span class="inline_code">setupThrowExceptionOnExecute</span> stores an exception that the mock SQL statement will throw when <span class="inline_code">execute()</span> is called. We can force such events because our coding style allows us to pass a mock implementation through to the code that uses the statement. We still check that the right parameters are passed through to the connection and statement, and that <span class="inline_code">execute</span> and <span class="inline_code">close</span> are each called once. Our test ensures that this exception is caught and translated into a <span class="inline_code">MailingListException</span>. If this doesn't happen, then the code will step through from <span class="inline_code">addMember</span> to the <span class="inline_code">fail</span> call and throw an assertion error. Even with the extra complexity, the test is still precise and self-explanatory.</p> <p>To pass this test, we change the implementation to:</p> <pre><span class="deemphasised"> public class MailingList { public void addMember(Connection connection, String emailAddress, String name) throws MailingListException, SQLException { PreparedStatement statement = connection.prepareStatement(INSERT_SQL);</span> try { statement.setString(1, emailAddress); statement.setString(2, name); statement.execute(); } catch (SQLException ex) { if (ex.getErrorCode() == DatabaseConstants.UNIQUE_CONSTRAINT_VIOLATED) { throw new MailingListException("Email address exists"); } else { throw ex; } } finally { statement.close(); } <span class="deemphasised"> } }</span></pre> <p>We have added a <span class="inline_code">finally</span> block to make sure that the statement will always be closed, even if there are failures when using it. Interestingly, the code now also passes another test:</p> <pre><span class="deemphasised"> public void testPrepareStatementFailsForAdd() throws MailingListException, SQLException {</span> mockConnection.setupThrowExceptionOnPrepare(new SQLException("MockConnection")); mockConnection.setExpectedPrepareStatementString(MailingList.INSERT_SQL); mockStatement.setExpectedExecuteCalls(0); mockStatement.setExpectedCloseCalls(0); <span class="deemphasised"> try { list.addMember(mockConnection, EMAIL, NAME); fail("Should have thrown exception"); } catch (SQLException expected) { } mockConnection.verify(); mockStatement.verify(); }</span></pre> <p>which checks that we do not try to execute or close the statement should the connection fail, although we still need to confirm that we made the right <span class="inline_code">prepareStatement</span> request. </p> <h3>What have we learned?</h3> <p>One of the hardest things to test is the throwing and management of exceptions. I have seen a lot of code that does not clean up resources on failure, or does so but is too complicated. Incremental, test-first programming is very effective at focussing a developer's attention on correct error handling by simulating exceptions that can be expensive, or even impossible, to generate from the real libary. Of course, how careful we should be depends on the application; we may not need precise cleanup for a single-action utility, but we certainly do for a long-lived server.</p> <p>The other design point to note from this example is that I have distinguished between mailing list exceptions (the system works, but was used incorrectly) and SQL exceptions (there was a system failure). As the application grows, we might want to translate those SQL exceptions into application-specific exceptions to help us manage exception handling. So far, however, that isn't necessary. <br> </p> <h2>Remove a member from the list</h2> <p>The next task is to remove someone from the list, based on their email address. The test for a successful removal is:</p> <pre> public void testRemoveMember() throws MailingListException, SQLException { mockStatement.setupUpdateCount(1); <span class="deemphasised"> mockConnection.setExpectedPrepareStatementString(MailingList.DELETE_SQL); mockStatement.addExpectedSetParameters(new Object[] {EMAIL}); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1);</span> list.removeMember(mockConnection, EMAIL); <span class="deemphasised"> mockConnection.verify(); mockStatement.verify(); }</span> </pre> <p>Once again, we check that the right parameters are passed in and the right methods called. A successful removal should return an update count greater than 0, so we preload a return value in the statement object. </p> <p>We can also test removing someone who is not in the list by setting the update count to 0, which would imply that no rows match the email address in the deletion request. </p> <pre> public void testRemoveMissingMember() throws SQLException { mockStatement.setupUpdateCount(0); <span class="deemphasised"> mockConnection.setExpectedPrepareStatementString(MailingList.DELETE_SQL); mockStatement.addExpectedSetParameters(new Object[] {EMAIL}); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1); </span> try { list.removeMember(mockConnection, EMAIL); fail("Should have thrown exception"); } catch (MailingListException expected) { } <span class="deemphasised"> mockConnection.verify(); mockStatement.verify(); }</span></pre> <p>The implementation of <span class="inline_code">removeMember</span> that supports both these tests is:</p> <pre> public void removeMember(Connection connection, String emailAddress) throws MailingListException, SQLException<br> { PreparedStatement statement = connection.prepareStatement(DELETE_SQL); try { statement.setString(1, emailAddress); if (statement.executeUpdate() == 0) { throw new MailingListException("Could not find email address: " + emailAddress); } } finally { statement.close(); } }</pre> <h3>Refactoring the tests</h3> <p>At this point we should start to notice repetition in the unit tests. In particular, each action on the member list requires a particular setup for the mock sql objects, and we always verify the same two objects. We refactor these into some helper methods:</p> <pre> private void setExpectationsForAddMember() { setExpectationsForPreparedStatement(MailingList.INSERT_SQL, new Object[] {EMAIL, NAME}); } private void setExpectationsForRemoveMember() { setExpectationsForPreparedStatement(MailingList.DELETE_SQL, new Object[] {EMAIL}); } private void setExpectationsForPreparedStatement(String sqlStatement, Object[] parameters) { mockConnection.setExpectedPrepareStatementString(sqlStatement); mockStatement.addExpectedSetParameters(parameters); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1); } private void verifyJDBC() { mockConnection.verify(); mockStatement.verify(); } </pre> <p>This allows us to simplify our existing tests and make them easier to read, for example:</p> <pre><span class="deemphasised"> public void testRemoveMember() throws MailingListException, SQLException { mockStatement.setupUpdateCount(1); </span> setExpecationsForRemoveMember(); list.removeMember(mockConnection, EMAIL); verifyJDBC(); } <span class="deemphasised"> public void testRemoveMissingMember() throws SQLException { mockStatement.setupUpdateCount(0);</span> setExpectationsForRemoveMember(); try { list.removeMember(mockConnection, EMAIL); fail("Should have thrown exception"); } catch (MailingListException expected) { } verifyJDBC(); }</pre> <h3> What have we learned?</h3> <p>As the application grows, we find that the incremental costs of adding new tests falls as we start to benefit from the unit tests we have alread written. At first, the benefit is intellectual, we can write a new test by copying and altering what we already have. The next stage is to refactor the tests to remove duplication. Maintaining and refactoring tests turns out to be as important as maintaining the code they exercise. The tests must be agile enough to follow the code as it is grows and changes. </p> <p>That said, we must maintain a balance here and not refactor the tests until they become too obscure to read; this applies particularly to verifications. In our example, the two verifiable objects are obvious enough that we can move them into a method with a meangingful name. Elsewhere it may be better to leave the calls to <span class="inline_code">verify </span>in each test case to make it clear to the reader exactly what is being tested.</p> <h2>Show the members of the list</h2> <p>Our next requirement is to show all the members of the list. The first question is how can we tell that we've seen everyone, so we create an object to work through all the list members; let's call it <span class="inline_code">ListAction</span> for now. We will set up a mock database and ask the <span class="inline_code">ListAction</span> object to verify that it has received the values that we expect it to when called by the <span class="inline_code">MailingList</span> object.</p> <h3>Extracting a single row</h3> <p>The simplest first test is for a list with one member. We should ensure that the <span class="inline_code">ListAction</span> object receives the right member details, that the ResultSet is asked for the right column values and that <span class="inline_code">next()</span> is called twice. We also check the usual expectations about creating and closing a statement and executing the query. Our first test implements exactly this:</p> <pre> public void testListOneMember() throws SQLException { MockSingleRowResultSet mockResultSet = new MockSingleRowResultSet(); mockStatement.setupResultSet(mockResultSet); mockResultSet.addExpectedNamedValues( COLUMN_NAMES, new Object[] {EMAIL, NAME}); mockResultSet.setExpectedNextCalls(2); mockListAction.addExpectedMember(EMAIL, NAME); setExpectationsForListMembers(); list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); mockListAction.verify(); }</pre> <p>The value expectations in a <span class="inline_code">MockSingleRowResultSet</span> serve two purposes. They are checked to verify that the client has asked for exactly the right columns, either by name or position, and they store the values that are returned. It is arguable that these two functions should be split, but the pattern occurs often enough that we have implemented it in an <span class="inline_code">ExpectationMap</span>. The <span class="inline_code">ListAction</span> class maintains expectations about the name and email address of the members of the list. If we write enough code to fulfil just this test, we will have a sketch of the structure of the method: </p> <pre> public void applyToAllMembers(Connection connection, ListAction listAction) throws SQLException { Statement statement = connection.createStatement(); ResultSet results = statement.executeQuery(LIST_SQL); while (results.next()) { listAction.applyTo(results.getString("email_address"), results.getString("name")); } statement.close(); }</pre> <h3>Extracting more than one member</h3> <p>This first test proves that we can extract the right values from a valid row in a <span class="inline_code">ResultSet</span>. Once we have a test for this, our other tests can concentrate on other aspects, such as handling different numbers of valid rows. We do not need to re-test extraction in every case. For example, when testing for different numbers of list members we only need to set expectations for the number of times we call the <span class="inline_code">listAction</span> object, we do not need to check again that the right values have been received. This makes tests more precise and orthogonal, and so easier to understand when they fail. It also makes test cases and mock objects easier to write because each one does less.</p> <p>For example, the test for listing two members does not set any expectations for the list member name and email address. This version does set column names and some dummy values, but these are only necessary to avoid type casting errors when returning values from the <span class="inline_code">ResultSet</span>; they are also used to define the number of rows to return. The expectations we <em>do</em> set are concerned with the number of times various methods are called.</p> <pre> public void testListTwoMembers() throws SQLException { MockMultiRowResultSet mockResultSet = new MockMultiRowResultSet(); mockStatement.setupResultSet(mockResultSet); mockResultSet.setupColumnNames(COLUMN_NAMES); mockResultSet.setupRows(TWO_ROWS); mockResultSet.setExpectedNextCalls(3); mockListAction.setExpectedMemberCount(2); <span class="deemphasised"> setExpectationsForListMembers(); list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); mockListAction.verify();</span> }</pre> <p>The test for a <span class="inline_code">ResultSet</span> with no rows is even simpler, we set up no row values at all and we tell the mock <span class="inline_code">ListAction</span> object to expect not to be called. At this point, we can also factor out setting up the mock <span class="inline_code">ResultSet</span> into a helper method.</p> <pre> public void testListNoMembers() throws SQLException { MockMultiRowResultSet mockResultSet = makeMultiRowResultSet(); mockResultSet.setExpectedNextCalls(1); mockListAction.setExpectNoMembers(); <span class="deemphasised"> setExpectationsForListMembers(); list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); mockListAction.verify();</span> } </pre> <h3>Handling failures</h3> <p>Our simple implementation of <span class="inline_code">applyToAllMembers</span> can support all these tests, but we know that we still have to manage exceptions. We should write an additional test to confirm that we can cope if the <span class="inline_code">ResultSet</span> fails. As with the test for adding an existing member, we wrap the call to the <span class="inline_code">ListAction</span> object in a <span class="inline_code">try</span> block and fail if the exception is not thrown. We also use the same technique as before to tell the mock <span class="inline_code">ResultSet</span> to throw an exception when anyone tries to get a value.</p> <pre><span class="deemphasised"> public void testListResultSetFailure() { MockMultiRowResultSet mockResultSet = makeMultiRowResultSet(); mockResultSet.setupRows(TWO_ROWS);</span> mockResultSet.setupThrowExceptionOnGet(new SQLException("Mock Exception")); mockResultSet.setExpectedNextCalls(1); mockListAction.setExpectNoMembers(); <span class="deemphasised"> setExpectationsForListMembers();</span> try { list.applyToAllMembers(mockConnection, mockListMembers); fail("Should have thrown exception"); } catch (SQLException expected) { } <span class="deemphasised"> mockResultSet.verify(); mockListMembers.verify(); }</span> </pre> <p>One significant point about this test is that it clearly defines the behaviour of <span class="inline_code">applyToAllMembers</span> in the presence of failures. We do not, for example, try to carry on should a record fail. Developing test-first and publishing the tests gives users of the code a much clearer description of how to use it than most conventional documentation.</p> <p>Our implementation will not pass this test because it has no <span class="inline_code">finally</span> clause, so we add it now.</p> <pre><span class="deemphasised"> public void applyToAllMembers(Connection connection, ListAction listAction) throws SQLException { Statement statement = connection.createStatement();</span> try { <span class="deemphasised"> ResultSet results = statement.executeQuery(LIST_SQL); while (results.next()) { listAction.applyTo(results.getString("email_address"), results.getString("name")); }</span> } finally { statement.close(); } <span class="deemphasised"> }</span> </pre> <p>We might also add tests to check that we correctly handle failures when the connection cannot create a statement or the statement cannot execute the query. For a long-lived server, this sort of error handling avoids resource leakages that can be very difficult to find in production.</p> <h3>Presenting the results</h3> <p>Now we can ask a <span class="inline_code">MailingList</span> object to process all the members of the list by passing an object of type <span class="inline_code">ListAction</span>. We might present the results by implementing a <span class="inline_code">ConvertToMemberNode</span> class that adds each member's details to an XML document. That rendering is managed by another class, <span class="inline_code">ListDocument</span>, that is passed in when starting the action.</p> <pre> class ConvertToMemberNode implements ListAction {<br> ListDocument listDocument; public ConvertToMemberNode(ListDocument aListDocument) { listDocument = aListDocument; } public void applyTo(String email, String name) { listDocument.startMember(); listDocument.addMemberEmail(email); listDocument.addMemberName(name); listDocumnet.endMember(); } }</pre> <p>Of course this new class should have its own set of unit tests. </p> <h3>What have we learned?</h3> <p>A common difficulty when testing networks of objects is the cost of setting up the test environment. These techniques show that we can manage this by being very precise about what each test verifies, and by providing just enough mock implementation to get us through. Teasing out the different aspects of what we are testing to make the tests more orthogonal has several advantages: the tests are more precise, so errors are easier to find; smaller tests are easier to implement and easier to read; and, the mock implementations can be more specialised and so simpler to implement. </p> <p>An alternative approach is to use one of the available stub JDBC implementations, or even a scratch copy of a real database. This is a good technique when getting started with unit testing or when retrofitting tests to an existing code base, but the mock object approach still has some advantages: </p> <ul> <li>the setup is cheaper, which makes the tests easier to maintain and quicker to run; </li> <li>failures can happen as soon as something unexpected happens, rather than verifying the result afterwards; </li> <li>we can verify behaviour such as the number of times times <span class="inline_code">close()</span> is called; and, </li> <li>we can force unusual conditions such as SQL exceptions.</li> </ul> <h2>Send a message to all the members</h2> <p>Our final requirement is to send a message to all the members of the list. We can do this by providing another implementation of the <span class="inline_code">ListAction </span> interface, in this case a <span class="inline_code">AddMemberToMessage</span> class. We have to make a design choice here about how to handle any messaging exceptions: we can either stop immediately, or collect the failures and process them at the end; for now, we shall stop processing immediately. The <span class="inline_code">Message</span> object is set up by the calling code, which then sends it to all the recipients.</p> <pre> class AddMembersToMessage implements ListAction { Message message; public AddMemberToMessage(Message aMessage) { message = aMessage; } public void applyTo(String email, String name) throws MailingListException { try { message.addRecipient(RecipientType.TO, new InternetAddress(email, name)); } catch (MessagingException ex) { throw new MailingListException("Adding member to message", email, name, ex); } } } </pre> <p>The <span class="inline_code">MessagingException</span> exception is checked, so we have to handle it here or propagate it through the code. We want to keep the <span class="inline_code">ListAction </span> interface generic, so we translate the <span class="inline_code">MessagingException</span> to a <span class="inline_code">MailingListException</span>, and propagate that instead. Now we have a new failure mode, so we write an additional unit test that ensures that we clean up properly after a <span class="inline_code">MailingListException</span>.</p> <pre><span class="deemphasised"> public void testListmembersFailure() throws SQLException { MockMultiRowResultSet mockResultSet = makeMultiRowResultSet(); mockResultSet.setupRows(TWO_ROWS);</span> mockListAction.setupThrowExceptionOnMember(new MailingListException()); mockResultSet.setExpectedNextCalls(1); <span class="deemphasised"> setExpectationsForListMembers(); try { list.applyToAllMembers(mockConnection, mockListAction); fail("Should have thrown exception");</span> } catch (MailingListException expected) { <span class="deemphasised"> } mockResultSet.verify(); mockListAction.verify(); }</span> </pre> <h3>Tell, don't ask</h3> <p>This approach to test-first development has led to a solution that may appear needlessly complex, as against just returning a collection of members from the <span class="inline_code">MailingList</span>. Our experience, however, is that code written this way is much more flexible. For example, if we later decide to ignore message errors and keep sending, we can do so by collecting them in the <span class="inline_code">AddMemberToMessage</span> class and processing them after the message has been sent. Similarly, if our mailing list becomes very large, we can start to process recipients in small batches and do not have to worry about creating excessively large collections of list members. On the other hand, if we do want to return a list of members, we can still write another <span class="inline_code">ListAction</span> implementation that appends each member to a collection.</p> <h2>Summary</h2> <p>This example shows how far you can you can isolate your unit tests from each other and avoid external dependencies by adopting Mock Objects. More interestingly, this approach to unit testing leads your code towards a style that is both flexible and easy to test, including testing system exceptions. As an application grows, common test infrastructure gets pushed into the mock implementations and the incremental cost of adding new tests drops substantially. In our experience, the first few tests with a complex new library, such as JDBC, can be slow to write but the mock implementations stabilise very quickly—particularly when using run-time proxies or an IDE to generate stub methods.</p> <hr> <p>© Steve Freeman, 2001</p> <p> </p> </body> </html> |
From: Steve F. <sm...@us...> - 2001-09-08 16:16:40
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers In directory usw-pr-cvs1:/tmp/cvs-serv11568/papers Log Message: Directory /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers added to the repository |