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-08-31 17:34:25
|
Hi Steve, ----- Original Message ----- From: "Steve Freeman" <st...@m3...> To: <moc...@li...> Sent: Thursday, August 30, 2001 10:33 PM Subject: Re: [Mockobjects-java-dev] CVS: mockobjects-java/doc/xdocs doc-book.xml,1.8,1.9 index.xml,1.12,1.13 > > > From: "Vincent Massol" <vm...@oc...> > > > > 2 things : > > > > * if you modify doc-book.xml, you should also modify site-book.xml (you > > can > > > > factorise the 2 if you wish with an XML fragment) > > > > > > it was only punctuation, no contents. > > > > still the 2 files need to be kept in sync. > > do you mean the version numbers, or something? I mean that if you add a menu item or change a label in one of these 2 files you need to reflect that change to the other one because they are not sharing an XML fragment at the currrent time (or factorise them using an XML fragment ...). > > > > sorry, it was late. we have to find something else, then, because What Is > > It? doesn't really make sense. > > > > Yep, I agree, I was not very pleased with "What is it ?" either ... :) > > Some propositions : > > - Mock Objects > > - Getting Started > > - Philosophy > > - The Story > > - A definition > > - Introduction > > how about Introduction? fine with me. > S. > 2¡rjzS,´'yuS²q®z¶-²¢-³²~¡n¶½ -Vincent #{#~[{[|{|[|`[{~{#~#%µ¨£µ¨ (my signature is not as nice as yours ... :) ) |
From: Steve F. <st...@m3...> - 2001-08-31 14:06:34
|
PiA+IEZyb206ICJWaW5jZW50IE1hc3NvbCIgPHZtYXNzb2xAb2N0by5jb20+DQo+ID4gPiAyIHRo aW5ncyA6DQo+ID4gPiAqIGlmIHlvdSBtb2RpZnkgZG9jLWJvb2sueG1sLCB5b3Ugc2hvdWxkIGFs c28gbW9kaWZ5IHNpdGUtYm9vay54bWwgKHlvdQ0KPiBjYW4NCj4gPiA+IGZhY3RvcmlzZSB0aGUg MiBpZiB5b3Ugd2lzaCB3aXRoIGFuIFhNTCBmcmFnbWVudCkNCj4gPg0KPiA+IGl0IHdhcyBvbmx5 IHB1bmN0dWF0aW9uLCBubyBjb250ZW50cy4NCj4gDQo+IHN0aWxsIHRoZSAyIGZpbGVzIG5lZWQg dG8gYmUga2VwdCBpbiBzeW5jLg0KDQpkbyB5b3UgbWVhbiB0aGUgdmVyc2lvbiBudW1iZXJzLCBv ciBzb21ldGhpbmc/DQoNCj4gPiBzb3JyeSwgaXQgd2FzIGxhdGUuIHdlIGhhdmUgdG8gZmluZCBz b21ldGhpbmcgZWxzZSwgdGhlbiwgYmVjYXVzZSBXaGF0IElzDQo+IEl0PyBkb2Vzbid0IHJlYWxs eSBtYWtlIHNlbnNlLg0KPiANCj4gWWVwLCBJIGFncmVlLCBJIHdhcyBub3QgdmVyeSBwbGVhc2Vk IHdpdGggIldoYXQgaXMgaXQgPyIgZWl0aGVyIC4uLiA6KQ0KPiBTb21lIHByb3Bvc2l0aW9ucyA6 DQo+IC0gTW9jayBPYmplY3RzDQo+IC0gR2V0dGluZyBTdGFydGVkDQo+IC0gUGhpbG9zb3BoeQ0K PiAtIFRoZSBTdG9yeQ0KPiAtIEEgZGVmaW5pdGlvbg0KPiAtIEludHJvZHVjdGlvbg0KDQpob3cg YWJvdXQgSW50cm9kdWN0aW9uPw0KUy4NCg== |
From: Steve F. <sm...@us...> - 2001-08-28 22:58:23
|
Update of /cvsroot/mockobjects/doc In directory usw-pr-cvs1:/tmp/cvs-serv25018 Modified Files: another_route.html Log Message: Another couple of lines... Index: another_route.html =================================================================== RCS file: /cvsroot/mockobjects/doc/another_route.html,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- another_route.html 2001/08/27 17:50:07 1.2 +++ another_route.html 2001/08/28 22:58:19 1.3 @@ -113,8 +113,10 @@ target code, as Mock Objects, rather than use techniques such as inner classes to give us access to private data. This means that we tend to define our assertions at the beginning of test, rather than at the end.</p> -<p><br> -</p> +<h2>More functionality</h2> +<h3>No record for the email address</h3> +<p>People are likely to request passwords for email addresses that are not in + the database, either by accident or maliciously. </p> <hr> <p>© Steve Freeman, 2001</p> <p> </p> |
From: Steve F. <sm...@us...> - 2001-08-28 22:57:27
|
Update of /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/password In directory usw-pr-cvs1:/tmp/cvs-serv24838 Modified Files: TestForgotPasswordServlet.java Log Message: Changed test name Index: TestForgotPasswordServlet.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/password/TestForgotPasswordServlet.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- TestForgotPasswordServlet.java 2001/08/27 11:20:24 1.2 +++ TestForgotPasswordServlet.java 2001/08/28 22:57:23 1.3 @@ -39,7 +39,7 @@ mockReminder.verify(); } - public void testNoPasswordForEmail() throws ServletException, IOException { + public void testEmailNotFound() throws ServletException, IOException { mockReminder.setupEmailNotFound(); mockReminder.setExpectedEmailAddress(EMAIL); |
From: Steve F. <sm...@us...> - 2001-08-27 17:50:10
|
Update of /cvsroot/mockobjects/doc In directory usw-pr-cvs1:/tmp/cvs-serv2889 Modified Files: another_route.html Log Message: end of the first test Index: another_route.html =================================================================== RCS file: /cvsroot/mockobjects/doc/another_route.html,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- another_route.html 2001/08/27 11:21:20 1.1 +++ another_route.html 2001/08/27 17:50:07 1.2 @@ -19,6 +19,10 @@ <h1>Another approach to working backwards</h1> <p align="center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> +<p>The "Chicago School" of writing mock objects is interestingly different + from the approach we take in London. To examine these differences, I have rewritten + the password reminder example taken from XP in Practice.</p> +<h2>The first test</h2> <h3>Send a reminder</h3> <p>The first test assumes success and should check that an email is sent and that the user is redirected to a success page. How can we test these? Redirects are @@ -72,8 +76,45 @@ passwordReminder.sendReminder(emailAddress); response.sendRedirect(SENT_URI + "?" + EMAIL_PARAM + "=" + emailAddress); } +</pre> +<h3>A mock password reminder</h3> +<p>The servlet code tells us that the interface for a password reminder class + needs just a single method.</p> +<pre> public interface PasswordReminder { + public void sendReminder(String emailAddress); + }</pre> +<p>The implementation of a mock password reminder is very simple; it is based + on an expectation class from the mockobjects library.</p> +<pre> public class MockPasswordReminder extends MockObject implements PasswordReminder { + private ExpectationValue emailAddress = + new ExpectationValue("MockPasswordReminder email"); + + public void sendReminder(String anEmailAddress) { + emailAddress.setActual(anEmailAddress); + } - </pre> + public void setExpectedEmailAddress(String anEmailAddress) { + emailAddress.setExpected(anEmailAddress); + } + } +</pre> +<p>An <span class="inline_code">ExpectationValue</span> will throw an assertion + failed exception if the value passed to the <span class="inline_code">setActual()</span> + method is not equal to the value passed to the <span class="inline_code">setExpected()</span> + method. It will also fail when calling <span class="inline_code">verify()</span>, + inherited from <span class="inline_code">MockObject</span>, if no actual value + has been set during the test. An expectation defines an interaction that should + take place between the target code and the mock object if the test runs successfully—something + between an invariant and post-condition. </p> +<h3>Differences</h3> +<p>There are two obvious differences to the original so far. First, we start at + the top level of the problem, from outside the servlet, rather than from an + internal object. Second, we perfer to pass our test infrastructure into the + target code, as Mock Objects, rather than use techniques such as inner classes + to give us access to private data. This means that we tend to define our assertions + at the beginning of test, rather than at the end.</p> +<p><br> +</p> <hr> <p>© Steve Freeman, 2001</p> <p> </p> |
From: Vincent M. <vm...@oc...> - 2001-08-27 11:32:30
|
----- Original Message ----- From: "Steve Freeman" <st...@m3...> To: <moc...@li...> Sent: Monday, August 27, 2001 12:01 PM Subject: Re: [Mockobjects-java-dev] CVS: mockobjects-java/doc/xdocs doc-book.xml,1.8,1.9 index.xml,1.12,1.13 > From: "Vincent Massol" <vm...@oc...> > > 2 things : > > * if you modify doc-book.xml, you should also modify site-book.xml (you can > > factorise the 2 if you wish with an XML fragment) > > it was only punctuation, no contents. still the 2 files need to be kept in sync. > > > * I had put "What are Mock Objects" as the title but removed it because it > > was too large (I seem to remember). Have you tried it by generating the doc > > sorry, it was late. we have to find something else, then, because What Is It? doesn't really make sense. Yep, I agree, I was not very pleased with "What is it ?" either ... :) Some propositions : - Mock Objects - Getting Started - Philosophy - The Story - A definition - Introduction .... -Vincent |
From: Steve F. <st...@m3...> - 2001-08-27 11:22:24
|
RnJvbTogIlZpbmNlbnQgTWFzc29sIiA8dm1hc3NvbEBvY3RvLmNvbT4NCj4gMiB0aGluZ3MgOg0K PiAqIGlmIHlvdSBtb2RpZnkgZG9jLWJvb2sueG1sLCB5b3Ugc2hvdWxkIGFsc28gbW9kaWZ5IHNp dGUtYm9vay54bWwgKHlvdSBjYW4NCj4gZmFjdG9yaXNlIHRoZSAyIGlmIHlvdSB3aXNoIHdpdGgg YW4gWE1MIGZyYWdtZW50KQ0KDQppdCB3YXMgb25seSBwdW5jdHVhdGlvbiwgbm8gY29udGVudHMu DQoNCj4gKiBJIGhhZCBwdXQgIldoYXQgYXJlIE1vY2sgT2JqZWN0cyIgYXMgdGhlIHRpdGxlIGJ1 dCByZW1vdmVkIGl0IGJlY2F1c2UgaXQNCj4gd2FzIHRvbyBsYXJnZSAoSSBzZWVtIHRvIHJlbWVt YmVyKS4gSGF2ZSB5b3UgdHJpZWQgaXQgYnkgZ2VuZXJhdGluZyB0aGUgZG9jDQoNCnNvcnJ5LCBp dCB3YXMgbGF0ZS4gd2UgaGF2ZSB0byBmaW5kIHNvbWV0aGluZyBlbHNlLCB0aGVuLCBiZWNhdXNl IFdoYXQgSXMgSXQ/IGRvZXNuJ3QgcmVhbGx5IG1ha2Ugc2Vuc2UuDQoNCg0K |
From: Steve F. <sm...@us...> - 2001-08-27 11:21:23
|
Update of /cvsroot/mockobjects/doc In directory usw-pr-cvs1:/tmp/cvs-serv14020 Added Files: another_route.html Log Message: Initial copy of new paper --- NEW FILE: another_route.html --- <html> <head> <title>Another approach to working backwards</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>Another approach to working backwards</h1> <p align="center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> <h3>Send a reminder</h3> <p>The first test assumes success and should check that an email is sent and that the user is redirected to a success page. How can we test these? Redirects are managed by the <span class="inline_code">HttpServletResponse</span>, so we use a mock implementation and tell it to expect to be redirected to the relevant URL. For the reminder email, we need an object that can verify that it has been asked to send an email to a given address. We don't know exactly what it will do just yet, but we do know its inputs and, more or less, when it will get called. Let's add a placeholder, <span class="inline_code">MockPasswordReminder</span>, to confirm that our servlet behaves correctly. We also decide to call the servlet a <span class="inline_code">ForgotPasswordServlet</span>. Now we can write our first test.</p> <pre> public void testReminderEmailSent() throws ServletException, IOException { mockRequest.setupAddParameter(ForgotPasswordServlet.EMAIL_PARAM, EMAIL); mockReminder.setExpectedEmailAddress(EMAIL); mockResponse.setExpectedRedirect(SENT_URI + "?email=" + EMAIL); passwordServlet.doGet(mockRequest, mockResponse); mockResponse.verify(); mockReminder.verify(); }</pre> <p>This test says:</p> <ul> <li>set the email parameter on the inbound request to a value, <span class="inline_code">EMAIL</span>, that we can check for later. The <span class="inline_code">mockRequest</span> is a fake implementation of an <span class="inline_code">HttpServletRequest</span> that responds as if it were had come from a browser request.</li> <li>tell our <span class="inline_code">MockPasswordReminder</span> to expect to be called with the given email address.</li> <li>tell our <span class="inline_code">mockResponse</span> to expect to be redirected to the success URL with the email address as a parameter. Again, the <span class="inline_code">mockResponse</span> is a fake implementation that acts as if it had been supplied by the servlet engine. </li> <li>call the servlet object directly, passing in the mock implementations of the objects it needs; this is where we actually run the code under test. In this case, we also call the </li> <li>verify that the expectations we set at the beginning of the test have been fulfilled.</li> </ul> <p>The servlet will be passed the request and response but it has to store the fake reminder object so, for now, we assign it in a servlet constructor. I will address how to replace this with the real reminder object later. The first implementation of the servlet method is:</p> <pre> protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String emailAddress = request.getParameter(EMAIL_PARAM); passwordReminder.sendReminder(emailAddress); response.sendRedirect(SENT_URI + "?" + EMAIL_PARAM + "=" + emailAddress); } </pre> <hr> <p>© Steve Freeman, 2001</p> <p> </p> </body> </html> |
From: Steve F. <sm...@us...> - 2001-08-27 11:20:26
|
Update of /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/password In directory usw-pr-cvs1:/tmp/cvs-serv13887 Modified Files: ForgotPasswordServlet.java TestForgotPasswordServlet.java Added Files: StubServletConfig.java Log Message: Moved password reminder hook to servlet constructor --- NEW FILE: StubServletConfig.java --- package com.mockobjects.examples.password; import java.util.Enumeration; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import junit.framework.AssertionFailedError; public class StubServletConfig implements ServletConfig { public String getInitParameter(String s) { throw new AssertionFailedError("not expected: " + s); } public Enumeration getInitParameterNames() { throw new AssertionFailedError("not expected"); } public ServletContext getServletContext() { throw new AssertionFailedError("not expected"); } public String getServletName() { throw new AssertionFailedError("not expected"); } } Index: ForgotPasswordServlet.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/password/ForgotPasswordServlet.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- ForgotPasswordServlet.java 2001/08/21 00:16:27 1.1 +++ ForgotPasswordServlet.java 2001/08/27 11:20:23 1.2 @@ -15,8 +15,8 @@ private PasswordReminder passwordReminder; - public void init(ServletConfig config, PasswordReminder aPasswordReminder) throws ServletException { - super.init(config); + public ForgotPasswordServlet(PasswordReminder aPasswordReminder) { + super(); passwordReminder = aPasswordReminder; } @@ -33,7 +33,7 @@ } private void redirectFor(String baseName, HttpServletResponse response, String emailAddress) throws IOException { - response.sendRedirect(getInitParameter(baseName) + "?email=" + emailAddress); + response.sendRedirect(getInitParameter(baseName) + "?" + EMAIL_PARAM + "=" + emailAddress); } } Index: TestForgotPasswordServlet.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/password/TestForgotPasswordServlet.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- TestForgotPasswordServlet.java 2001/08/21 00:16:27 1.1 +++ TestForgotPasswordServlet.java 2001/08/27 11:20:24 1.2 @@ -1,36 +1,35 @@ package com.mockobjects.examples.password; -import java.util.Enumeration; import java.io.IOException; import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; import javax.servlet.ServletException; -import junit.framework.AssertionFailedError; import com.mockobjects.servlet.MockHttpServletRequest; import com.mockobjects.servlet.MockHttpServletResponse; import com.mockobjects.util.TestCaseMo; public class TestForgotPasswordServlet extends TestCaseMo { public static final String SENT_URI = "sent_uri"; - public static final String EMAIL_FAILED_URI = "bademail_uri"; - public static final String EMAIL_NOT_FOUND_URI = "noemail_uri"; + public static final String EMAIL_FAILED_URI = "bad_email_uri"; + public static final String EMAIL_NOT_FOUND_URI = "no_email_uri"; public static final String EMAIL = "em...@an...dress"; public TestForgotPasswordServlet(String name) { super(name); } - private ForgotPasswordServlet passwordServlet = new ForgotPasswordServlet(); private MockHttpServletRequest mockRequest = new MockHttpServletRequest(); private MockHttpServletResponse mockResponse = new MockHttpServletResponse(); private MockPasswordReminder mockReminder = new MockPasswordReminder(); + private ForgotPasswordServlet passwordServlet; public void setUp() throws ServletException, IOException { + passwordServlet = new ForgotPasswordServlet(mockReminder); + passwordServlet.init(createStubServletConfig()); + mockRequest.setupAddParameter(ForgotPasswordServlet.EMAIL_PARAM, EMAIL); - passwordServlet.init(createStubServletConfig(), mockReminder); } - public void testReminderEmailSent() throws ServletException, IOException { + public void testReminderEmailSent() throws ServletException, IOException { mockReminder.setExpectedEmailAddress(EMAIL); mockResponse.setExpectedRedirect(SENT_URI + "?email=" + EMAIL); @@ -40,7 +39,7 @@ mockReminder.verify(); } - public void testNoPasswordForEmail() throws ServletException, IOException { + public void testNoPasswordForEmail() throws ServletException, IOException { mockReminder.setupEmailNotFound(); mockReminder.setExpectedEmailAddress(EMAIL); @@ -53,7 +52,7 @@ } private ServletConfig createStubServletConfig() { - return new ServletConfig() { + return new StubServletConfig() { public String getInitParameter(String s) { if (ForgotPasswordServlet.SENT_PARAM_NAME.equals(s)) { return SENT_URI; @@ -62,19 +61,7 @@ } else if (ForgotPasswordServlet.EMAIL_NOT_FOUND_PARAM_NAME.equals(s)) { return EMAIL_NOT_FOUND_URI; } - throw new AssertionFailedError("not expected: " + s); - } - - public Enumeration getInitParameterNames() { - throw new AssertionFailedError("not expected"); - } - - public ServletContext getServletContext() { - throw new AssertionFailedError("not expected"); - } - - public String getServletName() { - throw new AssertionFailedError("not expected"); + return super.getInitParameter(s); } }; } |
From: Steve F. <sm...@us...> - 2001-08-27 09:41:19
|
Update of /cvsroot/mockobjects/doc In directory usw-pr-cvs1:/tmp/cvs-serv23393 Modified Files: jdbc_testfirst.html Log Message: Intro, conclusion and some fixes Index: jdbc_testfirst.html =================================================================== RCS file: /cvsroot/mockobjects/doc/jdbc_testfirst.html,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- jdbc_testfirst.html 2001/08/11 23:51:16 1.9 +++ jdbc_testfirst.html 2001/08/27 09:41:15 1.10 @@ -5,25 +5,38 @@ <style type="text/css"> <!-- .deemphasised { color: #666666} -h1 { text-align: center; font-family: Arial, Helvetica, sans-serif} -h3 { font-family: Arial, Helvetica, sans-serif; font-style: italic; font-weight: normal; font-size: medium} -h2 { font-family: Arial, Helvetica, sans-serif; font-style: normal; font-weight: normal; line-height: 1.5} -.inline_code { font-family: "Courier New", Courier, mono; font-style: italic; font-size: smaller} +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> +<p align="center">Steve Freeman <tt> <st...@m3...></tt></p> <h2>Introduction</h2> -<p>Many developers find unit testing third-party components such as databases - tricky 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>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 <tt>http://www.mockobjects.com</tt>. 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 @@ -40,13 +53,13 @@ <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 the certain things to happen:</p> + 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 once to make the insertion</li> - <li>close the statement once</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() { @@ -77,11 +90,12 @@ } </pre> <p></p> -<p>Note that <span class="inline_code">testAddNewMember</span> declares SQLException - in its throws clause. This means that any SQLException 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>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(); @@ -103,12 +117,16 @@ <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 succesful +<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. Of course, we - will need an integration test elsewhere in the system, but this approach separates - the two aspects (what we say to the database, and what the database expects - to hear) which makes it easier to find errors as the system grows. </p> + 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, @@ -214,7 +232,7 @@ </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 succesful removal is:</p> + The test for a successful removal is:</p> <pre> public void testRemoveMember() throws MailingListException, SQLException { mockStatement.setupUpdateCount(1); @@ -230,10 +248,11 @@ }</span> </pre> <p>Once again, we check that the right parameters are passed in and the right - methods called. A succesful removal should return an update count greater than + 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.</p> + 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); @@ -314,7 +333,7 @@ <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 + 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> @@ -326,14 +345,14 @@ 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 collect - list members; let's call it <span class="inline_code">ListMembers</span> for - now. We will set up a mock database and ask the <span class="inline_code">ListMembers</span> + 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">ListMembers</span> object receives the right member + 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> @@ -346,31 +365,31 @@ COLUMN_NAMES, new Object[] {EMAIL, NAME}); mockResultSet.setExpectedNextCalls(2); - mockListMembers.addExpectedMember(EMAIL, NAME); + mockListAction.addExpectedMember(EMAIL, NAME); setExpectationsForListMembers(); - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); - mockListMembers.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">listMembers</span> class maintains expectations + 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, ListMembers listMembers) throws SQLException { +<pre> public void applyToAllMembers(Connection connection, ListAction listAction) throws SQLException { Statement statement = connection.createStatement(); ResultSet results = statement.executeQuery(LIST_SQL); while (results.next()) { - listMembers.member(results.getString("email_address"), results.getString("name")); + listAction.applyTo(results.getString("email_address"), results.getString("name")); } statement.close(); }</pre> @@ -380,7 +399,7 @@ 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">listMembers</span> + 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 @@ -389,7 +408,7 @@ 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 do + 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(); @@ -400,31 +419,31 @@ mockResultSet.setExpectedNextCalls(3); - mockListMembers.setExpectedMemberCount(2); + mockListAction.setExpectedMemberCount(2); <span class="deemphasised"> setExpectationsForListMembers(); - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); - mockListMembers.verify();</span> + 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">ListMembers</span> + 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); - mockListMembers.setExpectNoMembers(); + mockListAction.setExpectNoMembers(); <span class="deemphasised"> setExpectationsForListMembers(); - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); - mockListMembers.verify();</span> + mockListAction.verify();</span> } </pre> <h3>Handling failures</h3> @@ -432,7 +451,7 @@ 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">ListMembers</span> object in a <span class="inline_code">try</span> + <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> @@ -442,7 +461,7 @@ mockResultSet.setupThrowExceptionOnGet(new SQLException("Mock Exception")); mockResultSet.setExpectedNextCalls(1); - mockListMembers.setExpectNoMembers(); + mockListAction.setExpectNoMembers(); <span class="deemphasised"> setExpectationsForListMembers();</span> try { @@ -461,12 +480,12 @@ 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, ListMembers listMembers) throws SQLException { +<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()) { - listMembers.member(results.getString("email_address"), results.getString("name")); + listAction.applyTo(results.getString("email_address"), results.getString("name")); }</span> } finally { statement.close(); @@ -478,21 +497,21 @@ 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>We can now 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">ListMembers</span>. - In a servlet, we might present the results by implementing a <span class="inline_code">MembersAsTableRow</span> - class that writes out each member as a row in an HTML table; the <span class="inline_code">PrintWriter</span> - would be passed through from the request's <span class="inline_code">ServletResponse</span>.</p> -<pre> class MembersAsTableRow implements ListMembers {<br> PrintWriter writer; - public MembersAsTableRow(PrintWriter aPrintWriter) { - writer = aPrintWriter; - } - public void member(String email, String name) { - writer.print("<TR><TD>"); - writer.print(email); - writer.print("</TD><TD>"); - writer.print(name); - writer.println("</TD></TR>"); +<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> @@ -520,19 +539,19 @@ </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">ListMembers</span> - interface, in this case a <span class="inline_code">AddMembersToMessage</span> + 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 ListMembers { + class AddMembersToMessage implements ListAction { Message message; - public AddMembersToMessage(Message aMessage) { + public AddMemberToMessage(Message aMessage) { message = aMessage; } - public void member(String email, String name) throws MailingListException { + public void applyTo(String email, String name) throws MailingListException { try { message.addRecipient(RecipientType.TO, new InternetAddress(email, name)); @@ -544,25 +563,25 @@ </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">ListMembers</span> interface generic, so we translate + 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> - mockListMembers.setupThrowExceptionOnMember(new MailingListException()); + mockListAction.setupThrowExceptionOnMember(new MailingListException()); mockResultSet.setExpectedNextCalls(1); <span class="deemphasised"> setExpectationsForListMembers(); try { - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); fail("Should have thrown exception");</span> } catch (MailingListException expected) { <span class="deemphasised"> } mockResultSet.verify(); - mockListMembers.verify(); + mockListAction.verify(); }</span> </pre> <h3>Tell, don't ask</h3> @@ -571,17 +590,23 @@ <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">AddMembersToMessage</span> class and processing + 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 excessive 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">ListMembers</span> implementation that adds each member - to a collection.</p> + 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> -<h3>Notes</h3> -All the code in these examples and the mock objects library are available for -download from <tt>http://www.mockobjects.com</tt><br> +<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> |
From: Vincent M. <vm...@oc...> - 2001-08-27 07:32:57
|
Hi Steve, 2 things : * if you modify doc-book.xml, you should also modify site-book.xml (you can factorise the 2 if you wish with an XML fragment) * I had put "What are Mock Objects" as the title but removed it because it was too large (I seem to remember). Have you tried it by generating the doc ? -Vincent ----- Original Message ----- From: "Steve Freeman" <sm...@us...> To: <moc...@li...> Sent: Monday, August 27, 2001 3:43 AM Subject: [Mockobjects-java-dev] CVS: mockobjects-java/doc/xdocs doc-book.xml,1.8,1.9 index.xml,1.12,1.13 > Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs > In directory usw-pr-cvs1:/tmp/cvs-serv4550 > > Modified Files: > doc-book.xml index.xml > Log Message: > Small tidy-ups > > Index: doc-book.xml > =================================================================== > RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/doc-book.xml,v > retrieving revision 1.8 > retrieving revision 1.9 > diff -u -r1.8 -r1.9 > --- doc-book.xml 2001/08/21 11:41:27 1.8 > +++ doc-book.xml 2001/08/27 02:43:33 1.9 > @@ -17,7 +17,7 @@ > </menu> > > <menu label="Documentation"> > - <menu-item label="What is it ?" source="endotesting.xml"/> > + <menu-item label="What are Mock Objects?" source="endotesting.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.12 > retrieving revision 1.13 > diff -u -r1.12 -r1.13 > --- index.xml 2001/08/11 14:51:59 1.12 > +++ index.xml 2001/08/27 02:43:33 1.13 > @@ -27,14 +27,14 @@ > A core mock objects framework. This is a library of code that > supports the implementation of mock objects. It is based around > a set of expectation classes for values and collections. There are > - also various other classes to make mock objects easier to write or to > + also various other classes to make mock objects easier to write or to > use. > </li> > <li> > A methodology for developing and using mock objects. > </li> > <li> > - A default set of mock implementation for the standard Java platform > + A default set of mock implementation for the standard Java platform > APIs. We have made a start on packages such as servlets, sql, and io. > </li> > <li> > @@ -50,12 +50,12 @@ > <p> > Our larger goal is to make > <link href="www.mockobjects.com">www.mockobjects.com</link> > - <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 > - because it has a stable set of API's that are amenable to writing > - Mock Objects. We intend to apply these techniques to > - other languages and environments. > + <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 > + because it has a stable set of APIs that are suitable for writing > + Mock Objects. We have applied these techniques to > + other environments and intend to publish the results here. > </p> > </s1> > > @@ -67,7 +67,7 @@ > <td> > The Mock Objects project now has a user web site. > We are committed to building > - a thriving framework and community around Mock Objects! > + a thriving framework and community around Mock Objects. > Of course we're looking for help ... > </td> > </tr> > > > _______________________________________________ > Mockobjects-java-dev mailing list > Moc...@li... > http://lists.sourceforge.net/lists/listinfo/mockobjects-java-dev > |
From: Steve F. <sm...@us...> - 2001-08-27 02:43:37
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv4550 Modified Files: doc-book.xml index.xml Log Message: Small tidy-ups Index: doc-book.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/doc-book.xml,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- doc-book.xml 2001/08/21 11:41:27 1.8 +++ doc-book.xml 2001/08/27 02:43:33 1.9 @@ -17,7 +17,7 @@ </menu> <menu label="Documentation"> - <menu-item label="What is it ?" source="endotesting.xml"/> + <menu-item label="What are Mock Objects?" source="endotesting.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.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- index.xml 2001/08/11 14:51:59 1.12 +++ index.xml 2001/08/27 02:43:33 1.13 @@ -27,14 +27,14 @@ A core mock objects framework. This is a library of code that supports the implementation of mock objects. It is based around a set of expectation classes for values and collections. There are - also various other classes to make mock objects easier to write or to + also various other classes to make mock objects easier to write or to use. </li> <li> A methodology for developing and using mock objects. </li> <li> - A default set of mock implementation for the standard Java platform + A default set of mock implementation for the standard Java platform APIs. We have made a start on packages such as servlets, sql, and io. </li> <li> @@ -50,12 +50,12 @@ <p> Our larger goal is to make <link href="www.mockobjects.com">www.mockobjects.com</link> - <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 - because it has a stable set of API's that are amenable to writing - Mock Objects. We intend to apply these techniques to - other languages and environments. + <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 + because it has a stable set of APIs that are suitable for writing + Mock Objects. We have applied these techniques to + other environments and intend to publish the results here. </p> </s1> @@ -67,7 +67,7 @@ <td> The Mock Objects project now has a user web site. We are committed to building - a thriving framework and community around Mock Objects! + a thriving framework and community around Mock Objects. Of course we're looking for help ... </td> </tr> |
From: Steve F. <sm...@us...> - 2001-08-27 02:42:56
|
Update of /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/mailinglist In directory usw-pr-cvs1:/tmp/cvs-serv4373 Modified Files: MailingList.java TestMailingList.java Added Files: ListAction.java MockListAction.java Removed Files: ListMembers.java MockListMembers.java Log Message: Changed the name of ListMembers to ListAction in the mailing list example. --- NEW FILE: ListAction.java --- package com.mockobjects.examples.mailinglist; public interface ListAction { public void applyTo(String email, String name) throws MailingListException; } --- NEW FILE: MockListAction.java --- package com.mockobjects.examples.mailinglist; import com.mockobjects.MockObject; import com.mockobjects.ExpectationList; import com.mockobjects.MapEntry; import com.mockobjects.ExpectationCounter; public class MockListAction extends MockObject implements ListAction { private ExpectationList members = new ExpectationList("MockListAction.members"); private ExpectationCounter memberCount = new ExpectationCounter("MockListAction.count"); private MailingListException memberException = null; public void applyTo(String email, String name) throws MailingListException { memberCount.inc(); if (null != memberException) { throw memberException; } members.addActual(constructEntry(email, name)); } public void addExpectedMember(String email, String name) { members.addExpected(constructEntry(email, name)); } public void setExpectedMemberCount(int count) { memberCount.setExpected(count); } public void setExpectNoMembers() { memberCount.setExpectNothing(); members.setExpectNothing(); } public void setupThrowExceptionOnMember(MailingListException exception) { memberException = exception; } private Object constructEntry(String email, String name) { return new MapEntry(email, name); } } Index: MailingList.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/mailinglist/MailingList.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- MailingList.java 2001/08/11 23:50:22 1.4 +++ MailingList.java 2001/08/27 02:42:54 1.5 @@ -36,13 +36,13 @@ } } - public void applyToAllMembers(Connection connection, ListMembers listMembers) throws MailingListException, SQLException { + public void applyToAllMembers(Connection connection, ListAction listMembers) throws MailingListException, SQLException { Statement statement = connection.createStatement(); try { ResultSet results = statement.executeQuery(LIST_SQL); while (results.next()) { - listMembers.member(results.getString("email_address"), results.getString("name")); + listMembers.applyTo(results.getString("email_address"), results.getString("name")); } } finally { statement.close(); Index: TestMailingList.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/examples/com/mockobjects/examples/mailinglist/TestMailingList.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- TestMailingList.java 2001/08/11 23:50:22 1.9 +++ TestMailingList.java 2001/08/27 02:42:54 1.10 @@ -19,7 +19,7 @@ private MailingList list = new MailingList(); - private MockListMembers mockListMembers = new MockListMembers(); + private MockListAction mockListAction = new MockListAction(); private MockConnection mockConnection = new MockConnection(); private MockPreparedStatement mockPreparedStatement = new MockPreparedStatement(); private MockStatement mockStatement = new MockStatement(); @@ -95,14 +95,14 @@ MockMultiRowResultSet mockResultSet = makeMultiRowResultSet(); mockResultSet.setExpectedNextCalls(1); - mockListMembers.setExpectNoMembers(); + mockListAction.setExpectNoMembers(); setExpectationsForListMembers(); - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); - mockListMembers.verify(); + mockListAction.verify(); } public void testListOneMember() throws MailingListException, SQLException { @@ -111,14 +111,14 @@ mockResultSet.addExpectedNamedValues(COLUMN_NAMES, ONE_ROW); mockResultSet.setExpectedNextCalls(2); - mockListMembers.addExpectedMember(EMAIL, NAME); + mockListAction.addExpectedMember(EMAIL, NAME); setExpectationsForListMembers(); - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); - mockListMembers.verify(); + mockListAction.verify(); } public void testListTwoMembers() throws MailingListException, SQLException { @@ -126,32 +126,32 @@ mockResultSet.setupRows(TWO_ROWS); mockResultSet.setExpectedNextCalls(3); - mockListMembers.setExpectedMemberCount(2); + mockListAction.setExpectedMemberCount(2); setExpectationsForListMembers(); - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); verifyJDBC(); mockResultSet.verify(); - mockListMembers.verify(); + mockListAction.verify(); } public void testListmembersFailure() throws SQLException { MockMultiRowResultSet mockResultSet = makeMultiRowResultSet(); mockResultSet.setupRows(TWO_ROWS); - mockListMembers.setupThrowExceptionOnMember(new MailingListException()); + mockListAction.setupThrowExceptionOnMember(new MailingListException()); mockResultSet.setExpectedNextCalls(1); setExpectationsForListMembers(); try { - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); fail("Should have thrown exception"); } catch (MailingListException expected) { } mockResultSet.verify(); - mockListMembers.verify(); + mockListAction.verify(); } public void testListResultSetFailure() throws MailingListException { @@ -160,29 +160,29 @@ mockResultSet.setupThrowExceptionOnGet(new SQLException("Mock Exception")); mockResultSet.setExpectedNextCalls(1); - mockListMembers.setExpectNoMembers(); + mockListAction.setExpectNoMembers(); setExpectationsForListMembers(); try { - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); fail("Should have thrown exception"); } catch (SQLException expected) { } mockResultSet.verify(); - mockListMembers.verify(); + mockListAction.verify(); } public void testListStatementFailure() throws MailingListException { mockStatement.setupThrowExceptionOnExecute(new SQLException("Mock exception")); - mockListMembers.setExpectNoMembers(); + mockListAction.setExpectNoMembers(); setExpectationsForListMembers(); try { - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); fail("Should have thrown exception"); } catch (SQLException expected) { } - mockListMembers.verify(); + mockListAction.verify(); } public void testListConnectionFailure() throws MailingListException { @@ -192,14 +192,14 @@ mockStatement.setExpectedCloseCalls(0); mockStatement.setExpectedExecuteCalls(0); - mockListMembers.setExpectNoMembers(); + mockListAction.setExpectNoMembers(); try { - list.applyToAllMembers(mockConnection, mockListMembers); + list.applyToAllMembers(mockConnection, mockListAction); fail("Should have thrown exception"); } catch (SQLException expected) { } - mockListMembers.verify(); + mockListAction.verify(); } private MockMultiRowResultSet makeMultiRowResultSet() { --- ListMembers.java DELETED --- --- MockListMembers.java DELETED --- |
From: Steve F. <sm...@us...> - 2001-08-27 01:14:36
|
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects In directory usw-pr-cvs1:/tmp/cvs-serv16642 Modified Files: Expectation.java ExpectationCollection.java Verifiable.java Log Message: added comments to expectations interfaces Index: Expectation.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/Expectation.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Expectation.java 2001/07/29 19:50:24 1.1 +++ Expectation.java 2001/08/27 01:14:31 1.2 @@ -1,10 +1,34 @@ package com.mockobjects; +/** + * An <EM>Expectation</EM> is an object that we set up at the beginning of a unit test to + * expect certain things to happen to it. If it is possible to tell, the Expectation will + * fail as soon as an incorrect value has been set. + * + * Call verify() at the end of a unit test to check for missing or incomplete values. + * + * If no expectations have been set on the object, then no checking will be done and + * verify() will do nothing. + */ public interface Expectation extends Verifiable { + /** + * Return true if any expectations have been set on this object. + */ public boolean hasExpectations(); + /** + * Tell the object to expect nothing to happen to it, perhaps because the test is exercising + * the handling of an error. The Expectation will fail if any actual values are set. + * + * Note that this is not the same as not setting any expectations, in which case verify() + * will do nothing. + */ void setExpectNothing(); + /** + * If an incorrect actual value is set, defer reporting this as a failure until verify() + * is called on this object. + */ public void setFailOnVerify(); } Index: ExpectationCollection.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/ExpectationCollection.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- ExpectationCollection.java 2001/07/29 19:50:24 1.1 +++ ExpectationCollection.java 2001/08/27 01:14:31 1.2 @@ -2,6 +2,14 @@ import java.util.*; +/** + * An <EM>ExpectationCollection</EM> is an expectation that supports multiple values, such as lists + * and sets. + * + * The addition methods distinguish between adding a single value and unpacking the contents of + * a collection. We have to make this distinction so that it is possible to add an array, enumeration, + * or iterator as a single expected object, rather than adding its contents. + */ public interface ExpectationCollection extends Expectation { void addActual(Object actual); @@ -11,6 +19,7 @@ void addActualMany(Enumeration actuals); void addActualMany(Iterator actuals); + void addExpected(Object expected); Index: Verifiable.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/Verifiable.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Verifiable.java 2001/07/29 19:50:24 1.1 +++ Verifiable.java 2001/08/27 01:14:31 1.2 @@ -1,6 +1,15 @@ package com.mockobjects; +/** + * A <em>Verifiable</em> is an object that can confirm at the end of a unit test that + * the correct behvaiour has occurred. + * + * @see com.mockobjects.util.Verifiable to check all the Verifiables in an object. + */ public interface Verifiable { + /** + * Throw an AssertionFailedException if any expectations have not been met. + */ public abstract void verify(); } |
From: Steve F. <st...@m3...> - 2001-08-25 00:22:12
|
Q2FuIHlvdSBzdGFydCB0byBwdXQgdGhlbSBiYWNrPyANCg0KLS0tLS0gT3JpZ2luYWwgTWVzc2Fn ZSAtLS0tLSANCkZyb206IDxtYWN0YUBidGludGVybmV0LmNvbT4NClRvOiA8bW9ja29iamVjdHMt amF2YS1kZXZAbGlzdHMuc291cmNlZm9yZ2UubmV0Pg0KU2VudDogRnJpZGF5LCBBdWd1c3QgMjQs IDIwMDEgNDowNyBQTQ0KU3ViamVjdDogW01vY2tvYmplY3RzLWphdmEtZGV2XSBWZXJpZnkgdXNh Z2UgaW4gY29yZQ0KDQoNCj4gZm9yIHRob3NlIG9mIHVzIHRoYXQgc3RpbGwgaGF2ZSB0byBydW4g aW4gSkRLIDEuMS54IGNhbiB3ZSBjb250aW51ZSB0byBpbXBsZW1lbnQgYSB2ZXJpZnkgbWV0aG9k IGluIHRoZSBjb3JlIHNvdXJjZSBjb2RlLg0KPiANCj4gVGhlIGF1dG8tdmVyaWZ5IGlzIGNvb2wg YnV0IGl0IGRvZXNuJ3Qgd29yayBpbiBqZHNrMS4xLnggYW5kIGFzIHdlIGhhdmUgYWxyZWFkeSB3 cml0dGVuIHRoZSB2ZXJpZnkgbWV0aG9kcyB3aHkgYXJlIHRoZXkgYmVpbmcgcmVtb3ZlZD8NCj4g DQo+IFRpbQ0KPiANCj4gX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX18NCj4gTW9ja29iamVjdHMtamF2YS1kZXYgbWFpbGluZyBsaXN0DQo+IE1vY2tvYmplY3Rz LWphdmEtZGV2QGxpc3RzLnNvdXJjZWZvcmdlLm5ldA0KPiBodHRwOi8vbGlzdHMuc291cmNlZm9y Z2UubmV0L2xpc3RzL2xpc3RpbmZvL21vY2tvYmplY3RzLWphdmEtZGV2DQo+IA0K |
From: Steve F. <st...@m3...> - 2001-08-25 00:03:35
|
PiBJbiBNb2NrQ29ubmVjdGlvbiB0aGVyZSBpcyBhc3RyYW5nZSBuYW1pbmcgY29udmVudGlvbiAt IHNldEV4cGVjdGVkUHJlcGFyZVN0YXRlbWVudFN0cmluZz8NCj4gDQo+IFdoYXQgaXMgdGhlIFN0 cmluZyBzdWZmaXggZm9yPyBBcyBpdCB0YWtlcyBhIHN0cmluZyBwYXJhbWV0ZXIgLSB0aGUgdXN1 YWwgY29udmVudGlvbiBpcyBzZXRFeHBlY3RlZFByZXBhcmVTdGF0ZW1lbnQuDQoNClRoaXMgaXMg dGhlIHNxbCBzdHJpbmcgdXNlZCB0byBjb25zdHJ1Y3QgYSBwcmVwYXJlZCBzdGF0ZW1lbnQuIGl0 J3MgdG8gYXZvaWQgY29uZnVzaW5nIHRoaXMgd2l0aCBleHBlY3RpbmcgYW4gYWN0dWFsIHByZXBh cmVkIHN0YXRlbWVudCBvYmplY3QuDQoNCm1ha2UgbWUgYSBiZXR0ZXIgb2ZmZXIuDQoNClMNCg0K |
From: Steve F. <st...@m3...> - 2001-08-25 00:03:35
|
RnJvbTogPG1hY3RhQGJ0aW50ZXJuZXQuY29tPg0KPiBTdGV2ZSAtIGluIHByYWN0aWNlIHRoZSBN b2NrWFhYUmVzdWx0U2V0IG9iamVjdHMgd29yayB2ZXJ5IHdlbGwuDQoNCjotKQ0KDQo+IFdlIGFy ZSBtaXNpbmcgYSBNb2NrWmVyb1Jvd1Jlc3VsdFNldCAod2hpY2ggdG9vayBtZSB0d28gc2Vjb25k cyB0byBpbXBsZW1lbnQpIGFuZCB0aGVyZSBhYm91dCAzIHVzZWZ1bCBjb252ZW5pYW5jZSBtZXRo b2RzIHRoYXQgbWFrZSB0aGluZ3MgZ28gbW9yZSBzbW9vdGhseSAtIGJ1dCBJIHdvdWxkIHNheSB0 aHVtYnMgdXAuDQoNClRyeSBhIG11bHRpLXJvdyB3aXRoIGFuIGVtcHR5IGFycmF5Lg0KDQpBcmUg eW91IGdvaW5nIHRvIHRlbGwgbWUgd2hhdCB0aG9zZSBvdGhlciBtZXRob2RzIGFyZT8gLi4uDQog DQo+IEkgaGF2ZW4ndCBoYWQgYSBjaGFuY2UgdG8gcHJvcGVybHkgcmVhZCB0aGUgZG9jIHN0dWZm IHRob3VnaC4NCg0KdGhlbiBJIG11c3QgaGF2ZSBnb3Qgc29tZXRoaW5nIHJpZ2h0Lg0KDQpTdGV2 ZQ0KDQo= |
From: Steve F. <st...@m3...> - 2001-08-25 00:03:35
|
PiBIb3dldmVyLCBJIGRvIGFncmVlIHRoYXQgaWYgaXQgZG9lcyBub3QgY29zdCBtdWNoLCB3ZSBj YW4gbWFpbnRhaW4gSkRLIDEuMQ0KPiBjb21wYXRpYmlsaXR5LiBJIGRvbid0IGtub3cgd2hhdCB0 aGlzIGF1dG8tdmVyaWZ5IHN0dWZmIGlzIHNvIEkgY2FuJ3Qgc3BlYWsNCj4gZm9yIHRoYXQuDQoN Cm1vcmUgaW50ZXJlc3RpbmdseSwgc29tZSBqYXZhIGVtYmVkZGVkIGlzIGF0IGFyb3VuZCAxLjEu NCBhbmQsIHdvcnNlLCBkb2Vzbid0IHN1cHBvcnQgcmVmbGVjdGlvbi4gDQoNClRpbSwgaWYgeW91 IHdhbnQgdXMgdG8gYXZvaWQgYnJlYWtpbmcgeW91ciBjb2RlIGJhc2UgdGhlbiB5b3UnbGwgaGF2 ZSB0byBoZWxwIHVzIGJ5IGtlZXBpbmcgdXAgdG8gZGF0ZS4gTm9uZSBvZiB1cyBhcmUgdXNpbmcg PDEuMiwgYW5kIEknbSByZWdyZXR0aW5nIG5vdCBoYXZpbmcgbW92ZWQgc3BsYXNoIHRvIDEuMy4N Cg0KSXMgdGhlcmUgYW55b25lIGVsc2UgYXQgY29ubmV4dHJhIHdobyBjYW4gaGVscCB1cyBrZWVw IGluIHN5bmM/DQoNClN0ZXZlDQoNCg== |
From: Vincent M. <vm...@oc...> - 2001-08-24 17:55:02
|
----- Original Message ----- From: <ma...@bt...> To: <moc...@li...> Sent: Friday, August 24, 2001 6:24 PM Subject: Re: [Mockobjects-java-dev] UnsupportedOperationException > >MockNotImplementedException (for the not implemented methods) and > > MockNotSupportedException (for the not supported methods). > > I am happy with this suggestion. > > > I don't agree here ... (once more :) ). There has been 3 versions of java > > since 1.1 (1.2, 1.3 and now 1.4). It is normal to drop support for 1.1. > > *all* the I have seen have already dropped support for 1.1 or are seriously > > considering it ... Even JUnit is considering dropping it in its next > > version. > > The biggest code base of MockObjects is the Connextra code base, they are stuck in 1.1.8 at the moment - there is no compelling reason not to support 1.1.x as we aren't doing anything fancy. The only unique feature is auto-validation of verifiables, and in the core project it is no bit problem to keep the manual verify methods. > > I don't mind having to import a compatibility set of classes - and with careful use of non specific import lines (especially in java.util.*) we can do this. > > I think it is totally unfair to shaft Connextra on this especially as most of the work on Mock objects came from that team and they kindly donated the foundation of the current code base. As it is easy to support 1.1.x I think we should. > Tim, I agree with this except on the following points : * Donating is one thing, supporting is another. I see no other connextra person involved here ... they should speak up. To give you another example: although I have written and donated Cactus to Jakarta, if I were to leave for some time and come back , I would have to accept that others would have made some changes in the best interest of the project (which may conflict with my own interest). That's the hard part of open source ... * Donating to open source means relinquishing the full power you had on the code to other persons. If these other persons think otherwise and they are in majority, it's too bad for yourself. Again, I see no connextra persons who are committers and speaking here, apart from you. This lead me to believe that they are not using the Mock Objects project but rather using a branch of their own. Now if they plan to use it in the future, they need to participate if they wish to drive it. However, I do agree that if it does not cost much, we can maintain JDK 1.1 compatibility. I don't know what this auto-verify stuff is so I can't speak for that. My £0.02 -Vincent |
From: <ma...@bt...> - 2001-08-24 17:28:50
|
Steve - in practice the MockXXXResultSet objects work very well. We are mising a MockZeroRowResultSet (which took me two seconds to implement) and there about 3 useful conveniance methods that make things go more smoothly - but I would say thumbs up. I haven't had a chance to properly read the doc stuff though. Tim |
From: <ma...@bt...> - 2001-08-24 17:24:15
|
>MockNotImplementedException (for the not implemented methods) and > MockNotSupportedException (for the not supported methods). I am happy with this suggestion. > I don't agree here ... (once more :) ). There has been 3 versions of java > since 1.1 (1.2, 1.3 and now 1.4). It is normal to drop support for 1.1. > *all* the I have seen have already dropped support for 1.1 or are seriously > considering it ... Even JUnit is considering dropping it in its next > version. The biggest code base of MockObjects is the Connextra code base, they are stuck in 1.1.8 at the moment - there is no compelling reason not to support 1.1.x as we aren't doing anything fancy. The only unique feature is auto-validation of verifiables, and in the core project it is no bit problem to keep the manual verify methods. I don't mind having to import a compatibility set of classes - and with careful use of non specific import lines (especially in java.util.*) we can do this. I think it is totally unfair to shaft Connextra on this especially as most of the work on Mock objects came from that team and they kindly donated the foundation of the current code base. As it is easy to support 1.1.x I think we should. Tim |
From: Vincent M. <vm...@oc...> - 2001-08-24 15:46:59
|
----- Original Message ----- From: <ma...@bt...> To: <moc...@li...> Sent: Friday, August 24, 2001 4:23 PM Subject: [Mockobjects-java-dev] UnsupportedOperationException > UnsupportedOperationException is a java 2 class - can we have a MockDoesNotSupportOperation exception instead? > why not MockNotImplementedException (for the not implemented methods) and MockNotSupportedException (for the not supported methods). > I can't stress enough - please keep the codebase open enough that we can support jdk11x wherever it is possible - it makes our existing users of mock objects more able to port stuff. I don't agree here ... (once more :) ). There has been 3 versions of java since 1.1 (1.2, 1.3 and now 1.4). It is normal to drop support for 1.1. *all* the I have seen have already dropped support for 1.1 or are seriously considering it ... Even JUnit is considering dropping it in its next version. Even IBM has moved on and is now supporting JDK 1.3 in VAJava and other products ! I'm -1 to continue support for JDK 1.1 unless it is done at no cost but it should not hamper anything new. Users of JDK 1.1 will simply need to use version such of the Mock Objects project and will not be able to benefit from the latest improvements. In France, we have a saying that goes "One can't have the butter and the money you bought it with" (le beurre et l'argent du beurre) which is translated in english by "You can't have your cake and eat it" .... :) -Vincent |
From: <ma...@bt...> - 2001-08-24 15:42:53
|
In MockConnection there is astrange naming convention - setExpectedPrepareStatementString? What is the String suffix for? As it takes a string parameter - the usual convention is setExpectedPrepareStatement. Tim |
From: <ma...@bt...> - 2001-08-24 15:23:18
|
UnsupportedOperationException is a java 2 class - can we have a MockDoesNotSupportOperation exception instead? I can't stress enough - please keep the codebase open enough that we can support jdk11x wherever it is possible - it makes our existing users of mock objects more able to port stuff. Tim |
From: <ma...@bt...> - 2001-08-24 15:07:48
|
for those of us that still have to run in JDK 1.1.x can we continue to implement a verify method in the core source code. The auto-verify is cool but it doesn't work in jdsk1.1.x and as we have already written the verify methods why are they being removed? Tim |