Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv27388/doc/xdocs Added Files: htmlbook.xsl doc-book.xml testing_guis_1.xml notes.xml htmlbook.css extra_entities.xml brief_introduction.xml Log Message: --- NEW FILE: htmlbook.xsl --- <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:import href="file:///@xslpath@/html/docbook.xsl"/> <xsl:param name="html.stylesheet" select="'htmlbook.css'"/> </xsl:stylesheet> --- NEW FILE: doc-book.xml --- <?xml version="1.0"?> <!DOCTYPE book [ <!ENTITY % docbook SYSTEM "file:///@docpath@/dtd/docbookx.dtd"> <!ENTITY % extra_entities SYSTEM "file:///@docpath@/extra_entities.xml"> <!ENTITY chp_brief_intro SYSTEM "file:///@docpath@/brief_introduction.xml"> <!ENTITY testing_guis1 SYSTEM "file:///@docpath@/testing_guis_1.xml"> <!ENTITY notes SYSTEM "file:///@docpath@/notes.xml"> %docbook; %extra_entities; ]> <book> <title>No Stone Unturned</title> <bookinfo> <author> <firstname>Steve</firstname> <surname>Freeman</surname> <email>st...@m3...</email> </author> <author> <firstname>Nat</firstname> <surname>Pryce</surname> <email>nat...@b1...</email> </author> <copyright> <year>@year@</year> <holder>Steve Freeman</holder> </copyright> </bookinfo> &testing_guis1; ¬es; </book> --- NEW FILE: testing_guis_1.xml --- <part status="first cut"> <title>Developing Graphical Applications</title> <partintro> <para> It <emphasis>is</emphasis> possible to develop graphical applications test-first. It's not easy, because graphical user interfaces have to respond to people (who are less predictable and linear than we'd like) and so tend to have lots of global state that's hard to test in isolation. That, and the variable quality of some popular graphical toolkits, means that we have to think a bit harder we do for non-graphical applications. It took a while for the test-driven development community to figure out what to do, for a long time the answer was to make the presentation layer as thin as possible and to defer everything down to a business layer. That's probably why there are no tests for the graphical test runners for <application>JUnit</application>. This is still good advice but it doesn't feel good enough, given how much we say we believe in the benefits of writing the tests first. Now we have a better handle on how to work with graphical environments (or at least some of them). Let's see something working. </para> </partintro> <chapter> <title>A Search Dialogue</title> <para> Our customer has asked us to write our favourite demonstration application, a search dialogue that takes a search string and shows multiple lines of results. After some discussion, the story breaks down into the following tasks: </para> <procedure> <step> <para> The user clicks the <guibutton>Search</guibutton> button. There are no matches and the status bar shows <emphasis>No entries found</emphasis>. </para> </step> <step> <para> The user clicks the <guibutton>Search</guibutton> button, show one matching line. </para> </step> <step> <para> Run a search when the user hits <keysym>Return</keysym> in the search string field. </para> </step> <step> <para> The user clicks the <guibutton>Search</guibutton> button, there are no matches and any previous output is cleared. </para> </step> <step> <para> At the start of a search, clear the status bar. Also, set the cursor to the hourglass during the search. </para> </step> <step> <para> The user clicks the <guibutton>Search</guibutton> button, there are too many results to show in the available space, so show scrollbars. </para> </step> <step> <para> The user clicks the <guibutton>Search</guibutton> button, there are too many results to show in the available space, stop when there's a pageful before the scrollbars appear. </para> </step> <step> <para> There are too many results, as with the last task. Add a <guibutton>More</guibutton> button that will show all the results after the first page has been shown. </para> </step> <step> <para> Disable the <guibutton>More</guibutton> button unless there is more than a page ofresults to show. </para> </step> </procedure> <para> As always, the trick is to find a way to add little bits of functionality to the application so that we can see that we're always moving forward. What we don't do is write the whole user interface and model seperately and then try to glue them to together at the end. </para> </chapter> <chapter> <title>The first test</title> <sidebar> <para>A search with no results.</para> </sidebar> <section> <title>Basic scaffolding</title> <para> Sometimes the hardest thing in test-driven development is getting started (a bit like writing this book). What is the simplest first test for a graphical user interface that we can implement without taking too large a step; we don't want to let more than a few minutes go by before we see that test bar turn green again. As always, the trick is to be very clear about what we want to test. </para> <para> While we're waiting for a flash of genius, let's do something simple enough to understand. Let's start the first test and make sure that we can create an application object. </para> <programlisting> public class SearcherTest extends TestCase { public SearcherTest(String name) { super(name); } public void testNoMatchesFound() { Searcher searcher = new Searcher(); } } public class Searcher { }</programlisting> &redbar; <para> This doesn't do much, but it does at least force us to come up with <classname>Searcher</classname>, a name for our application, and names are always important. What else can we assert? We know that we'll need some components in our user interface to complete the first task, so let's check that they're present. We'll start with just one. </para> <programlisting> public void testNoMatchesFound() { Searcher searcher = new Searcher(); assertNotNull("Should be search button", findNamedComponent(searcher, "search button")); }</programlisting> <sidebar> <function>findNamedComponent()</function> is a helper method that searches the user interface structure for a component that has the given name, see <xref linkend="findNamedComponent" /> </sidebar> <para> Now our test won't compile because <classname>Searcher</classname> is not a <classname>java.awt.Container</classname>. We will need a top-level window component somewhere, so let's fix both problems by making <classname>Searcher</classname> extend <classname>javax.swing.JFrame</classname>. We'll also create a button for starting the search so that the test will pass. We can do all of this in the constructor. One day, as the application grows, we can extract it into something cleaner, but this will do for now. </para> <programlisting> public class Searcher extends JFrame { public Searcher() throws HeadlessException { super(); JButton searchButton = new JButton("Search"); searchButton.setName("search button"); getContentPane().add(searchButton); } }</programlisting> &greenbar; <para> I'm prepared to believe this unit test, but I'd really like to <emphasis>see</emphasis> something—we also believe in early integration testing. I'll tack on a <function>main</function> method and a call to <function>pack()</function>so that we can run our magnificent first application. (I've also sneaked in a <classname>WindowListener</classname> to catch the window closing event and shut things down cleanly. It's boilerplate to help me get through this demonstration, if you like you can treat it as an exercise for the reader.) </para> <programlisting> public class Searcher extends JFrame { public Searcher() throws HeadlessException { super("Searcher"); JButton searchButton = new JButton("Search"); searchButton.setName("search button"); getContentPane().add(searchButton); pack(); } static public void main(String[] args) { Searcher searcher = new Searcher(); searcher.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { Window w = e.getWindow(); w.setVisible(false); w.dispose(); System.exit(0); } }); searcher.setVisible(true); } }</programlisting> <para>Which looks like this:</para> <screenshot><mediaobject> <imageobject><imagedata fileref="images/gui_example1.gif" format="gif" /></imageobject> <caption><para>The first screen</para></caption> </mediaobject></screenshot> </section> <section> <title>Finishing the test</title> <para> Now we've assembled some core infrastructure, we can finish off the test with some meaningful behaviour. What exactly does this test have to prove to fulfill our first requirement? That the status bar shows an appropriate message after the user clicks <guibutton>Search</guibutton>. For example, </para> <programlisting> public void testNoMatchesFound() { Searcher searcher = new Searcher(); ((JButton)findNamedComponent(searcher, "search button")).doClick(); assertEquals("Should be status", "No entries found", ((JLabel)findNamedComponent(searcher, "status")).getText()); }</programlisting> <para>Of course this fails:</para> <screen> There was 1 error: 1) testNoMatchesFound(nostone.gui.SearcherTest) java.lang.NullPointerException at nostone.gui.SearcherTest.testNoMatchesFound(SearcherTest.java:19) FAILURES!!! Tests run: 1, Failures: 0, Errors: 1</screen> &redbar; <para> because we don't have a status bar. I'll add a suitable <classname>javax.swing.JLabel</classname>. Now it fails for a more interesting reason. </para> <screen> There was 1 failure: 1) testNoMatchesFound(nostone.gui.SearcherTest)junit.framework.AssertionFailedError: Should be status expected:<No entries found> but was:< > at nostone.gui.SearcherTest.testNoMatchesFound(SearcherTest.java:19) FAILURES!!!</screen> &redbar; <para> Now we have to add some behaviour so that the status bar is updated. In Swing, that's done with an <classname>ActionListener</classname>. In this case, the behaviour is simple, just set the contents of the status component. Here's the whole of the reworked constructor. </para> <programlisting> public Searcher() throws HeadlessException { super("Searcher"); JButton searchButton = new JButton("Search"); searchButton.setName("search button"); final JLabel statusBar = new JLabel(" "); statusBar.setName("status"); searchButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { statusBar.setText("No entries found"); } }); getContentPane().add(searchButton, BorderLayout.NORTH); getContentPane().add(statusBar, BorderLayout.SOUTH); pack(); }</programlisting> &greenbar; <para>and here's the evidence that it actually works.</para> <screenshot><mediaobject> <imageobject><imagedata fileref="images/gui_example2.gif" format="gif" /></imageobject> <caption><para>With a status message</para></caption> </mediaobject></screenshot> </section> <section> <title>What have we learned</title> <para> User interface testing is messy, but not impossible. Graphical frameworks are made up of code, just like other frameworks and, if they're not completely dysfunctional, it should be possible to exercise that code in isolation. As always, the skill is in being very clear about what will give you enough confidence to carry on adding to and changing your code, without letting mistakes through or slowing yourself down with brittle tests. In this case, we've decided that we don't care about layout, just that various components are present <emphasis>somewhere</emphasis> in the application. Obviously, the look of the application must be right (or good enough) before it can ship, but layout and behaviour often change at different rates while a system is in development—they might even be worked on by different people—and you might not want your implementation test suite to break because someone's reworked a data panel. </para> </section> </chapter> <appendix id="findNamedComponent" status="todo"> <title>Finding GUI components</title> </appendix> </part> --- NEW FILE: notes.xml --- <part status="first cut"> <title>To Do</title> <itemizedlist> <listitem><para>Tests in Ant</para></listitem> <listitem><para>bash mocks</para></listitem> <listitem><para><emphasis>DocBook</emphasis>Fix keysym, guibutton, co</para></listitem> </itemizedlist> </part> --- NEW FILE: htmlbook.css --- /* htmlbook.css, a stylesheet for htmlbook */ h1 { text-align: center; font-family: Arial, Helvetica, sans-serif; font-weight: bold} h2 { font-family: Arial, Helvetica, sans-serif; margin-top: 3%} h3 { font-family: Arial, Helvetica, sans-serif; font-style: italic; font-weight: bold} p { font-family: Arial, Helvetica, sans-serif} li { font-family: Arial, Helvetica, sans-serif } pre { margin-left: 5%; } --- NEW FILE: extra_entities.xml --- <!ENTITY redbar "<emphasis>Red Bar</emphasis>" > <!ENTITY greenbar "<emphasis>Green Bar</emphasis>" > --- NEW FILE: brief_introduction.xml --- <chapter> <title>How Mock Objects Happened</title> <section><title>Introduction</title> <para>Mock Objects is a development technique that lets you unit test classes that you didn't think you could <emphasis>and</emphasis> 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.</para> <para>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 <emphasis>inside</emphasis> those mock implementations so that you can validate the interactions between the objects in your test case as they occur.</para> <para>This article assumes that you are familiar with java and unit testing using JUnit.</para> </section> <section><title>Simple Unit Tests</title> <remark>Perhaps we should change from "I" to "we" in these examples to show that we are pair programming!</remark> <para>Let's dive straight into a worked example. I am writing a component to automatically direct robots through a warehouse. Given a starting point on a grid, it will find a route through the stacks to a given destination. How can I test this? The standard approach is to write some unit tests that create a robot, call some methods on it, and then check that the resulting state is what we expect. My tests will have to tell me, for example, that the robot arrives where it was directed to go and does not hit anything on the way. A easy test to start with is to check that a robot does not move if it is told to go to its current location: </para> <example><title>the first test for our robot</title> <programlisting>public class TestRobot { [...] public void setUp() { robot = new Robot(); } public void testGotoSamePlace() { final Position POSITION = new Position(1, 1); robot.setCurrentPosition(POSITION); robot.goTo(POSITION); assertEquals("Should be same", POSITION, robot.getPosition()); } }</programlisting> </example> <para>This tells me that the robot thinks has arrived where it should be, but it doesn't tell me anything about how it got there. It might have trundled all the way round the warehouse before returning to its original location. I would like to know that the Robot didn't move in this test. One option would be to store and retrieve the route it took after each <methodname>goto()</methodname>. For example:</para> <example><title>Testing our robot more rigorously</title> <programlisting>public void testGotoSamePlace() { final Position POSITION = new Position(0, 0); robot.setCurrentPosition(POSITION); robot.goTo(POSITION); assertEquals("Should be empty", 0, robot.getRecentMoveRequests().size()); assertEquals("Should be same", POSITION, robot.getPosition()); }</programlisting> </example> <para>My next test might be to move one point on the grid:</para> <example><title>Testing movement</title> <programlisting>public void testMoveOnePoint() { final Position DESTINATION = new Position(1, 0); robot.setCurrentPosition(new Position(0, 0)); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); List moves = robot.getRecentMoveRequests(); assertEquals("Should be one move", 1, moves.size()); assertEquals("Should be same move", new MoveRequest(1, MoveRequest.SOUTH), moves.get(1)); }</programlisting> </example> <para>As the tests become more complex, I would pull out some of the detail into helper methods to make the code more legible:</para> <example><title>Helper methods make tests more readable</title> <programlisting>public void testMoveALongWay() { final Position DESTINATION = new Position(34, 71); robot.setCurrentPosition(new Position(0, 0)); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); assertEquals("Should be same moves", makeExpectedLongWayMoves(), robot.getRecentMoves()); }</programlisting> </example> <para>Where <methodname>makeExpectedLongWayMoves()</methodname> returns another list of the moves I expect the robot to take for this test.</para> <para>There are problems with this approach to testing. First, tests like this are effectively small-scale integration tests, they set up pre-conditions and test post-conditions but they have no access to the code while it is running. If one of these tests fail, I will have to step through the code to find the problem because the assertions have been made <emphasis>after</emphasis> the call has finished.</para> <para>Second, to test this behaviour at all, I had to add some functionality to the production class, namely retaining all the <classname>MoveRequest</classname>s since the last <methodname>goto()</methodname>. I don't have any other immediate need for <methodname>getRecentMovesRequests()</methodname>. </para> <para>Third, although it's not the case here, test suites based on extracting history from objects tend to need lots of utilities for constructing and comparing collections of values. The need to write external code to manipulate an object is often a warning that its class is incomplete and that some behaviour should be moved from the utility to the class.</para> <para>Is there a better way? Can I find a style that will give me better error reporting and put the behaviour in the right place?</para> </section> <section><title>Factoring Out The Motor</title> <para>My robot is actually doing two things when moving through the warehouse: it chooses a route from where it currently to where it has to arrive, and it actually moves along the route it has chosen. If I separate these responsibilities into two objects, I can test that the object planning the route makes the correct requests to the object moving the robot, and, separately, test that the object moving the robot responds correctly to the requests set to it. </para> <para>Now I need to define an object model that gives me the separation of concerns I need for testing, but also makes sense in terms of my application domain. Aha! Robots have <emphasis>Motors</emphasis>. The Robot plans its route and passes control requests to its Motor; the Motor moves the robot in response to those requests. By intercepting the requests between the Robot and its Motor I can track what's happening inside the Robot. </para> <para> The first step is to define the interface that the Robot will use to control the Motor:</para> <example><title>The Motor interface</title> <programlisting>public interface Motor { void move(MoveRequest request); }</programlisting> </example> <para>Now I need to initialise a Robot with a Motor when I create it. Because I want to intercept the interaction between the Robot and its Motor I cannot let the Robot instantiate its own Motor; there would be no way to then intercept the Robot's movement requests. That leads me to pass a Motor instance to the Robot's constructor.</para> <para>I can now write my tests to create a Robot with an implementation of the <interfacename>Motor</interfacename> interface, that watches what's happening in the Robot, and complains as soon as something goes wrong. In fact, I will do this right now, before I start thinking about writing a real implementation of the Motor interface, so that I know my Robot implementation still works despite the extensive refactorings I have performed. The first test is now:</para> <example><title>Testing using a mock motor</title> <programlisting>public void testGotoSamePlace() { final Position POSITION = new Position(0, 0); robot.setCurrentPosition(POSITION); Motor mockMotor = new Motor() { public void move(MoveRequest request) { fail("There should be no moves in this test"); } }; robot.setMotor(mockMotor); robot.goTo(POSITION); assertEquals("Should be same", POSITION, robot.getPosition()); }</programlisting> </example> <para>In this test, if there is a bug in the Robot code and the Motor gets requested to move, the mock implementation of <methodname>move()</methodname> will fail immediately and stop the test; I no longer need to ask the Robot where it's been.</para> <para>Now I know that my Robot class works I can write a real implementation of the Motor interface:</para> <example><title>A real implementation of the Motor interface</title> <programlisting> public class OneSpeedMotor implements Motor { public void move(MoveRequest request) { turnBy(request.getTurn()); advance(request.getDistance()); } [...] } </programlisting> </example> <para>As my tests grow, I can refactor the various mock implementations into a single, more sophisticated MockMotor and use it throughout all the Robot tests; for example:</para> <example><title>Creating a mock motor class</title> <programlisting>public void MockMotor implements Motor { private ArrayList expectedRequests = new ArrayList(); public void move(MoveRequest request) { assert("Too many requests", this.expectedRequests.size() > 0); assertEquals("Should be next request", this.expectedRequests.remove(0), request); } public void addExpectedRequest(MoveRequest request) { this.expectedRequests.add(request); } public void verify() { assertEquals("Too few requests", 0, this.expectedRequests.size()); } }</programlisting> </example> <para>Which makes our tests look like:</para> <example><title>Testing our robot with mock motors</title> <programlisting>public class TestRobot { [...] static final Position ORIGIN = new Position(0, 0); public void setUp() { mockMotor = new MockMotor(); robot = new Robot(mockMotor); robot.setCurrentPosition(ORIGIN); } public void testGotoSamePlace() { robot.goTo(ORIGIN); assertEquals("Should be same", ORIGIN, robot.getPosition()); mockMotor.verify(); } public void testMoveOnePoint() { final Position DESTINATION = new Position(1, 0); mockRobot.addExpectedRequest(new MoveRequest(1, MoveRequest.SOUTH)); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); mockMotor.verify(); } public void testMoveALongWay() { final Position DESTINATION = new Position(34, 71); mockMotor.addExpectedRequests(makeExpectedLongWayMoveRequests()); robot.goto(DESTINATION); assertEquals("Should be destination", DESTINATION, robot.getPosition()); mockMotor.verify(); } } </programlisting> </example> </section> <section><title>What does this mean?</title> <para>My code moved in this direction because I was committed to unit testing but didn't want to record and expose unnecessary details about the state of my Robot (the <methodname>getRecentMoveRequests()</methodname> method). As a result, I have better unit tests and a cleaner interface to the Robot class. But even better than that, by following the needs of my tests I have actually ended up improving the object model of the Robot by separating the Robot from its Motor and defining the an abstract interface between the two. 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 its motor:</para> <example><title>Tracking distance with the decorator pattern</title> <programlisting> /** 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()); </programlisting> </example> <para>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 <classname>Navigator</classname> object to work out the route, and the Robot would link the two together.</para> <para>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. </para> </section> <section><title>Conclusions</title> <para>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 classes and APIs. Third, there are now several tools and libraries to help with the construction of Mock Objects. In particular, the <ulink url="http://www.mockobjects.com">www.mockobjects.com</ulink> site includes a library of expectation objects. </para> <para> The rest of this book will show you how to use Mock Objects and Test Driven Design in your development process. We will work through some real-world examples to show how Mock Objects can be used to test Java APIs, drive refactoring and eliminate dependencies on external components. And along the way we will annotate our examples with tips and warnings to help you improve your technique and avoid pitfalls. </para> <para> Mock Objects and Test Driven Design have changed the way we develop code, and changed it noticeably for the better. We hope that you can benefit from these techniques as well and that this book helps you to do so. </para> </section> </chapter> |