|
From: Steve F. <sm...@us...> - 2001-11-05 02:38:44
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers
In directory usw-pr-cvs1:/tmp/cvs-serv29591
Modified Files:
brief_introduction.html
Log Message:
more contents
Index: brief_introduction.html
===================================================================
RCS file: /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers/brief_introduction.html,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- brief_introduction.html 2001/11/03 00:33:31 1.3
+++ brief_introduction.html 2001/11/05 02:38:38 1.4
@@ -1,10 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
- <title>Developing JDBC applications test-first</title>
-
+ <title>How Mock Objects happened</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-
<style type="text/css">
<!--
.deemphasised { color: #666666}
@@ -19,34 +17,36 @@
</style>
</head>
<body bgcolor="#ffffff">
-<h1>How Mock Objects happen</h1>
+<h1>How Mock Objects happened</h1>
<p align="Center">Steve Freeman <tt> <st...@m3...></tt></p>
<h2>Introduction</h2>
-<p>Unit testing is a fundamental practice in Extreme Programming, but
-most nontrivial code is difficult to test in isolation. You need to
-make sure that you test one feature at a time, and you want to be
-notified as soon as any problem occurs. Normal unit testing is hard
-because you are trying to test the code from outside. </p>
-
-<p>Mock Objects is an approach to writing unit tests based on two key
-techniques: replace everything except the code under test with mock
-implementations that emulate the rest of the environement, and put the
-test assertions <em>inside</em> those mock implementations so that you
-can validate the interactions between the objects in your test
-case.</p>
+<p>Mock Objects is a development technique that lets you unit test
+classes that you didn't think you could <em>and</em> helps you write
+better code whilst doing so. This article uses a simple example to
+show a compressed history of how Mock Objects was discovered by
+refactoring from conventional unit tests and what its advantages
+are.</p>
+
+<p>Mock Objects is based on two key concepts: replace everything
+except the code under test with mock implementations that emulate the
+rest of the environement, and put the test assertions <em>inside</em>
+those mock implementations so that you can validate the interactions
+between the objects in your test case. </p>
-<p>This article assumes that you are familiar with java and unit testing using JUnit</p>
+<p>This article assumes that you are familiar with java and unit
+testing using JUnit.</p>
<h2>Simple unit tests</h2>
-<p>Most people start writing unit tests by creating an object, calling
-some methods on it, and then checking its state. For example, imagine
-that I'm writing a component automatically to direct robots through a
-warehouse. Given a start point on a grid, it will find a route through
-the stacks to a given destination. The tests will have to tell me that
-the robot arrives correctly and does not hit anything on the way. A
-first test might be: </p>
+<p>Let's begin with an example. Most people start writing unit
+tests by creating an object, calling some methods on it, and then
+checking its state. For example, imagine that I'm writing a component
+automatically to direct robots through a warehouse. Given a start
+point on a grid, it will find a route through the stacks to a given
+destination. The tests will have to tell me that the robot arrives
+correctly and does not hit anything on the way. A first test might be:
+</p>
<pre>public class TestRobot {
[...]
@@ -114,7 +114,7 @@
step through the code to find the problem because the assertions have
been made <em>after</em> the call has finished. Second, to test this
behaviour at all, I had to add some functionality to the production
-class, namely retaining all the MoveRequests since the last
+class, namely retaining all the <em>MoveRequest</em>s since the last
<em>goto()</em>. I don't have any other immediate need for
<em>getRecentMovesRequests()</em>. Third, although it's not the case here,
test suites based on extracting history from objects tend to need lots
@@ -128,10 +128,10 @@
<h2>Factoring out the motor</h2>
-<p>One thing I can be sure of is that the Robot contains a Motor that
-responds to these move requests. If I can intercept those requests, I
-can track what's happening in the Robot. A first step would be to
-define an interface to the Motor:</p>
+<p>One thing I can be sure of is that the Robot contains a
+<em>Motor</em> that responds to these move requests. If I can
+intercept those requests, I can track what's happening in the Robot. A
+first step would be to define an interface to the Motor:</p>
<pre>public interface Motor {
void move(MoveRequest request);
@@ -225,24 +225,86 @@
assertEquals("Should be destination", DESTINATION, robot.getPosition());
mockMotor.verify();
+ }
}</pre>
+
+<h2>What does this mean?</h2>
+
+<p>My code moved in this direction because I was committed to unit
+testing but didn't want to expose unnecessary details about the state
+of my Robot (the <em>getRecentMoveRequests()</em> method). As a
+result, I find that I have better unit tests and that I have clarified
+some of the internal structure of the Robot by adding a
+<em>Motor</em> interface. I now have the flexibility to subtitute a
+different Motor implementation, perhaps one that
+accelerates. Similarly, if I want to track the total distance a Robot
+travels, I can do so without changing the implementation of either the
+Robot or Motor:</p>
+
+<pre>
+/**
+ * A decorator that accumulates distances, then passes the request
+ * on to a real Motor.
+ */
+public class MotorTracker implements Motor {
+ private Motor realMotor;
+ private long totalDistance = 0;
+
+ public MotorTracker(Motor aRealMotor) {
+ realMotor = aRealMotor;
+ }
+ public void move(MoveRequest request) {
+ totalDistance += request.getDistance();
+ realMotor.move(request);
+ }
+ public long getTotalDistance() {
+ return totalDistance;
+ }
+}
+
+// When constructing the Robot, wrap the implementation of a
+// Motor that does the work in a MotorTracker.
+OneSpeedMotor realMotor = new OneSpeedMotor();
+MotorTracker motorTracker = new MotorTracker(realMotor);
+Robot = new Robot(motorTracker);
+// do some travelling here [...]
+println("Total distance was: " + motorTracker.getTotalDistance());
+</pre>
+
+<p>Neither changing the Motor implementation nor adding tracking
+functionality would have been so easy if I had stuck with a testing
+strategy based on stashing the intermediate route. The next step might
+be to introduce a <em>Navigator</em> object to work out the route,
+and the Robot would then link the two together.</p>
+
+<p>Tests based on Mock Objects usually conform to a pattern: setup any
+state, set expectations for the test, run the target code, and verify
+that your expectations have been met. This style makes tests easy to
+work with because they look similar and because all the interactions
+with an object are local to a test fixture; I have found myself
+contributing usefully to someone else's code base after only a few
+minutes with the tests. More importantly, however, I constantly find
+that the process of deciding what to verify in a test drives me to
+clarify the relationships between an object and its collaborators.
+The flex points I add to my code to provide support for testing turn
+out to be the flex points I need as the use of the code evolves.
+</p>
+
+<h2>Conclusions</h2>
+
+<p>This simple example shows how refactoring tests with some design
+principles in mind led to the discovery of an unusually fruitful
+development technique. Using Mock Objects in practice is slightly
+different. First, the process of writing a test usually involves
+defining which mock objects are involved, rather than extracting them
+afterwards. Second, in Java at least, there are the beginnings of a
+Mock Object library for common types. Third, there are now several
+tools and libraries to help with the construction of Mock Objects. In
+particular, the <a
+href="http://www.mockobjects.com">www.mockobjects.com</a> site
+includes a library of expectation objects.
+</p>
-<h2>What has this change achieved?</h2>
-<ul>
- <li><em>Refactored test infrastructure</em> As my tests grow, I find myself repeating my test
- infrastructure. I can push those repetitions into mock implementations, based on the
- expectation types that ship with the Mock Objects library. </li>
- <li><em>Failing early</em> A key experience of developing test-first is learning to make test
- failures self explanatory. Failing at the time an error occurs with a useful message,
- rather than at the end of the test, is a powerful tool.</li>
- <li><em>Driving design</em> Choosing which objects to verify in a test turns out to be an
- effective tool for deriving the structure of a class and its neighbours.</li>
- <li><em>Consistent test structure</em> Most unit tests in this style have a similar structure:
- set up the objects (pre-conditions), set the expected behaviour (invariants and
- post-conditions), exercise the object, and then verify whether the expectations were
- fufilled. This style makes tests easy to work with because they look similar and because
- all the interactions with an object are local to a test fixture.</li>
-</ul>
<hr>
<p>© Steve Freeman 2001</p>
</body>
|