From: Vincent M. <vm...@us...> - 2001-08-21 11:11:45
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv11809/xdocs Modified Files: endotesting.xml Log Message: original endo testing paper (PDF) available as a link in endotesting.xml Index: endotesting.xml =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/endotesting.xml,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- endotesting.xml 2001/08/05 20:13:19 1.1 +++ endotesting.xml 2001/08/21 11:11:43 1.2 @@ -17,10 +17,11 @@ <s1 title="Endo-Testing: Unit Testing with Mock Objects"> <note> - This paper was presented at the conference, "eXtreme Programming and + This paper was presented at the conference, "eXtreme Programming and Flexible Processes in Software Engineering - XP2000". <br/> (c) 2000 Tim Mackinnon, Steve Freeman, Philip Craig. To be published in - <em>XP eXamined</em> by Addison-Wesley. + <em>XP eXamined</em> by Addison-Wesley. The original paper is + available <link href="misc/mockobjects.pdf">here</link> (PDF format). </note> <p> @@ -124,7 +125,7 @@ validate the inputs they have received from our unit test. In the following example, <code>JUnitCreatorModel</code> is an object that generates test classes in the VisualAge workspace, and - <code>myMockPackage</code> and <code>myMockWorkspace</code> are Mock + <code>myMockPackage</code> and <code>myMockWorkspace</code> are Mock Object implementations of interfaces provided with VisualAge: </p> @@ -134,7 +135,7 @@ new MockType(EXISTING_CLASS_NAME)); myMockWorkspace.addPackage(mockPackage); - JUnitCreatorModel creatorModel = + JUnitCreatorModel creatorModel = new JunitCreatorModel(myMockWorkspace, PACKAGE_NAME); try { @@ -148,21 +149,21 @@ ]]></source> <p> - There are two important points to note here. First, this test - does not test VisualAge, it only tests one piece of code that we have - written or, with test-driven programming, are about to write. The - full behaviour is exercised during functional testing. Second, we - are not trying to rewrite VisualAge, only to reproduce those - responses that we need for a particular test. Most of the methods of - a mock implementation do nothing or just store values in local - collections. For example, the implementation of the class + There are two important points to note here. First, this test + does not test VisualAge, it only tests one piece of code that we have + written or, with test-driven programming, are about to write. The + full behaviour is exercised during functional testing. Second, we + are not trying to rewrite VisualAge, only to reproduce those + responses that we need for a particular test. Most of the methods of + a mock implementation do nothing or just store values in local + collections. For example, the implementation of the class <code>MockPackage</code> might include: </p> <source><![CDATA[ public class MockPackage implements Package { private Vector myContainedTypes = new Vector(); - ... + ... public void addContainedType(Type type) { myContainedTypes.add(type); } @@ -216,7 +217,7 @@ </p> </s2> - + <s2 title="Why use Mock Objects ?"> <s3 title="Localising Unit Tests"> @@ -236,7 +237,7 @@ </p> <p> - For example, we might have a method on a class + For example, we might have a method on a class <code>Employee</code> that updates names and details atomically : </p> @@ -280,8 +281,8 @@ ]]></source> <p> - This technique is very easy to implement in modern development - environments, especially given industry standard interfaces such + This technique is very easy to implement in modern development + environments, especially given industry standard interfaces such as JDBC. </p> @@ -294,8 +295,8 @@ single piece of functionality. A unit test that depends on complex system state can be difficult to set up, especially as the rest of the system develops. Mock Objects avoid such problems by - providing a lightweight emulation of the required system state. - Furthermore, the setup of complex state is localised to one + providing a lightweight emulation of the required system state. + Furthermore, the setup of complex state is localised to one Mock Object instead of scattered throughout many unit tests. </p> @@ -303,9 +304,9 @@ One of the authors of this chapter worked on a project tool that released code from VisualAge to another source control system. As the tool grew, it became increasingly hard to unit test because - the cost of resetting the environment rose dramatically. The + the cost of resetting the environment rose dramatically. The project tool was later refactored to use mock implementations of - both VisualAge and the source control system. The result was + both VisualAge and the source control system. The result was both easier to test and better structured. </p> @@ -315,10 +316,10 @@ <p> Some unit tests need to test conditions that are very difficult to - reproduce. For example, to test server failures we can write a - Mock Object that implements the local proxy for the server. Each - unit test can then configure the proxy to fail with an expected - problem and the developers can write client code to make the test + reproduce. For example, to test server failures we can write a + Mock Object that implements the local proxy for the server. Each + unit test can then configure the proxy to fail with an expected + problem and the developers can write client code to make the test pass. An example of this is </p> @@ -341,17 +342,17 @@ <p> With this approach, the mock server runs locally and fails in a controlled manner. The test has no dependencies on components - outside the development system and is insulated from other - possible real world failures. This style of test is repeated - for other types of failure, and the entire test suite documents + outside the development system and is insulated from other + possible real world failures. This style of test is repeated + for other types of failure, and the entire test suite documents the possible server failures that our client code can handle. </p> <p> In the case of an expensive widget, we define similar unit tests. We can configure the mock widget with the desired state and - check that it has been used correctly. For example, a unit test - that checks that the widget is polled exactly once when a + check that it has been used correctly. For example, a unit test + that checks that the widget is polled exactly once when a registration key is sent would be </p> @@ -383,24 +384,24 @@ <p> Domain objects often fail some time after an error occurs, which - is one reason that debugging can be so difficult. With tests that - query the state of a domain object, all the assertions are made - together after the domain code has executed. This makes it - difficult to isolate the exact point at which a failure occurred. - One of the authors of this chapter experienced such problems - during the development of a financial pricing library. The unit - tests compared sets of results after each calculation had - finished. Each failure required considerable tracing to isolate - its cause, and it was difficult to test for intermediate values + is one reason that debugging can be so difficult. With tests that + query the state of a domain object, all the assertions are made + together after the domain code has executed. This makes it + difficult to isolate the exact point at which a failure occurred. + One of the authors of this chapter experienced such problems + during the development of a financial pricing library. The unit + tests compared sets of results after each calculation had + finished. Each failure required considerable tracing to isolate + its cause, and it was difficult to test for intermediate values without breaking encapsulation. </p> <p> On the other hand, a mock implementation can test assertions each time it interacts with domain code, and so is more likely to fail - at the right time and generate a useful message. This makes it - easy to trace the specific cause of the failure, especially as - the failure message can also describe the difference between the + at the right time and generate a useful message. This makes it + easy to trace the specific cause of the failure, especially as + the failure message can also describe the difference between the expected and actual values. </p> @@ -431,13 +432,13 @@ <p> Without Mock Objects, each unit test tends to have its own set of assertions about the domain code. These may be refactored into - shared methods within a unit test, but the developer has to - remember to apply them to new tests. Such assertions are built - into Mock Objects and so are applied by default whenever the - object is used. As the suite of unit tests grows, a Mock Object - will be used throughout the system and its assertions applied to - new code. Similarly, as the developers discover new assertions - that need to be made, these can be added once in a Mock Object + shared methods within a unit test, but the developer has to + remember to apply them to new tests. Such assertions are built + into Mock Objects and so are applied by default whenever the + object is used. As the suite of unit tests grows, a Mock Object + will be used throughout the system and its assertions applied to + new code. Similarly, as the developers discover new assertions + that need to be made, these can be added once in a Mock Object where they will automatically apply to all existing tests. </p> @@ -446,14 +447,14 @@ assertions in their Mock Objects have failed unexpectedly. Usually this is a timely warning about a constraint that the programmers have forgotten, but sometimes this is because the failing - constraints are not always relevant. These cases suggest - candidates for refactoring of either the domain code or Mock - Objects, and help to push the developers toward a better + constraints are not always relevant. These cases suggest + candidates for refactoring of either the domain code or Mock + Objects, and help to push the developers toward a better understanding of the system. </p> </s4> - + </s3> <s3 title="Effects on Coding Style"> @@ -559,9 +560,9 @@ </p> <p> - Instead, we can write a handler object to reify this - dialogue between a writer and a <code>Person</code>. - <code>Person</code> would then have a method to pass its internal + Instead, we can write a handler object to reify this + dialogue between a writer and a <code>Person</code>. + <code>Person</code> would then have a method to pass its internal contents to a handler : </p> @@ -581,13 +582,13 @@ </p> <source><![CDATA[ - void testPersonHandling() { + void testPersonHandling() { myMockHandler.setExpectedName(NAME); myMockHandler.setExpectedAge(AGE); myMockHandler.setExpectedTelephone(TELEPHONE); - + myPerson.handleDetails(myMockHandler); - + myMockHandler.verify(); } ]]></source> @@ -602,13 +603,13 @@ void testPersonPrintHandler() { myMockPrintWriter.setExpectedOutputPattern( ".*" + NAME + ".*" + AGE + ".*" + TELEPHONE + ".*"); - + myPrintHandler.name(NAME); myPrintHandler.age(AGE); myPrintHandler.telephone(TELEPHONE); - + myPrintHandler.writeTo(myMockPrintWriter); - + myMockPrintWriter.verify(); } ]]></source> @@ -678,7 +679,7 @@ ]]></source> <p> - would then return to the <code>Person</code> class and adjust any + would then return to the <code>Person</code> class and adjust any method signatures to use the new interface : </p> @@ -690,7 +691,7 @@ <p> This approach ensures that the interface will be the minimum that - the domain code needs, following the Extreme Programming principle + the domain code needs, following the Extreme Programming principle of not adding features beyond our current understanding. </p> @@ -804,7 +805,7 @@ <source><![CDATA[ public class ExpectationList implements Verifiable { - public ExpectationList(String failureMessage); + public ExpectationList(String failureMessage); public void addExpected(Object expectedItem); public void addActual(Object actualItem); public void verify() throws AssertionFailedException; @@ -846,36 +847,36 @@ <s2 title="References"> - <p> - [Beck2000] K. Beck. <em>Extreme Programming.</em>Addison-Wesley, + <p> + [Beck2000] K. Beck. <em>Extreme Programming.</em>Addison-Wesley, 2000.<br/> [Binder2000] R. V. Binder. <em>Testing Object-Oriented Systems : Models, Patterns, and Tools</em>. Addison-Wesley, 2000.<br/> - [C2] Various Authors. <em>SingletonsAreEvil.</em> On-line at + [C2] Various Authors. <em>SingletonsAreEvil.</em> On-line at <link href="http://c2.com/cgi/wiki?SingletonsAreEvil"> http://c2.com/cgi/wiki?SingletonsAreEvil</link><br/> - [Gamma+1995] R. Gamma, R. Helm, R. Johnson, J. Vlissides. - <em>Design Patterns: Elements of Reusable Object-Oriented + [Gamma+1995] R. Gamma, R. Helm, R. Johnson, J. Vlissides. + <em>Design Patterns: Elements of Reusable Object-Oriented Software</em>. Addison-Wesley, 1995.<br/> - [Lieberherr+1989] K. J. Lieberherr, I. M. Holland. "Assuring - Good Style for Object-Oriented Programs." <em>IEEE Software</em> + [Lieberherr+1989] K. J. Lieberherr, I. M. Holland. "Assuring + Good Style for Object-Oriented Programs." <em>IEEE Software</em> Volume 6, Number 5, pp. 38-49, September 1989.<br/> - [Mackinnon2000] T. Mackinnon. <em>JunitCreator</em>. On-line at + [Mackinnon2000] T. Mackinnon. <em>JunitCreator</em>. On-line at <link href="http://www.xpdeveloper.com/cgi-bin/wiki.cgi?JunitCreator"> http://www.xpdeveloper.com/cgi-bin/wiki.cgi?JunitCreator</link>.<br/> - [Stroustrup1994] B. Stroustrup. <em>The Design and Evolution of + [Stroustrup1994] B. Stroustrup. <em>The Design and Evolution of C++</em>. Addison-Wesley, 1994.<br/> </p> - + </s2> <s2 title="Acknowledgments"> - + <p> - We would like to thank the reviewers and the following colleagues - for their contributions to this chapter: Tom Ayerst, Oliver Bye, - Richard Karcich, Matthew Cooke, Sven Howarth, Tung Mac, Peter Marks, - Ivan Moore, John Nolan, Keith Ray, Paul Simmons, and J. D. + We would like to thank the reviewers and the following colleagues + for their contributions to this chapter: Tom Ayerst, Oliver Bye, + Richard Karcich, Matthew Cooke, Sven Howarth, Tung Mac, Peter Marks, + Ivan Moore, John Nolan, Keith Ray, Paul Simmons, and J. D. Weatherspoon. </p> |