Mock Objects

Mock Objects

software construction Editors: Andy Hunt and Dave Thomas I The Pragmatic Programmers [email protected] I [email protected] Mock Objects Dave Thomas and Andy Hunt Yet sit and see; Minding true things by what their mockeries be. —Shakespeare, Henry V ne thing that makes unit-testing code drop off, and we all know where that leads. so hard is the way the real world Fortunately there’s a testing pattern that keeps intruding. If all we had to do can help. Using mock objects, you can test was code up tests for methods that code in splendid isolation, simulating all sort arrays or generate Fibonacci se- those messy real-world things that would O ries, life would be easy. But in the real otherwise make automated testing impossi- world we have to test code that uses data- ble. And, as with many other testing prac- bases, communications devices, user inter- tices, the discipline of using mock objects faces, and external applications. We might can improve your code’s structure. An example: Testing a servlet Servlets are chunks of code that a Web server manages. Requests to certain URLs are forwarded to a servlet container (or man- ager) such as Jakarta Tomcat (http://jakarta. apache.org/tomcat), which in turn invokes the servlet code. The servlet then builds a re- sponse that it sends back to the requesting browser. From the end user’s perspective, it’s just like accessing any other page. Figure 1 shows part of the source of a have to interface to devices that aren’t yet trivial servlet that converts temperatures available or simulate network errors that are from Fahrenheit to Celsius. Let’s quickly impossible to generate locally. This all con- step through its operation. When the servlet spires to stop our unit tests from being neat, container receives the request, it automati- self-contained (and orthogonal) chunks of cally invokes the servlet method doGet(), code. Instead, if we’re not careful, we find passing in two parameters, a request and a ourselves writing tests that end up initializ- response. (These are important for our test- ing nearly every system component just to ing later). The request parameter contains give the tests enough context to run. Not information about the request; the servlet’s only is this time consuming, it also intro- job is to fill in the response. The servlet’s duces a ridiculous amount of coupling into body gets the contents of the field “Fahren- the testing process: someone changes an in- heit” from the request, converts it to Cel- terface or a database table, and suddenly the sius, and writes the results back to the user. setup code for your poor little unit test dies The writing is done via a PrintWriter object, mysteriously. Even the best-intentioned de- which a factory method in the response ob- velopers become discouraged after this hap- ject provides. If an error occurs converting pens a few times. Eventually, testing starts to the number (perhaps the user typed “boo!” 22 IEEE SOFTWARE May/June 2002 0740-7459/02/$17.00 © 2002 IEEE SOFTWARE CONSTRUCTION into the form’s temperature field), we 1 public void doGet(HttpServletRequest req, catch the exception and report the er- 2 HttpServletResponse res) ror in the response. 3 throws ServletException, IOException Having written this code (or be- 4{ fore writing it, for those in the Ex- 5 String str_f = req.getParameter(“Fahrenheit”); treme Programming tribe), we’ll want 6 7 res.setContentType(“text/html”); a unit test to verify it. This is where 8 PrintWriter out = res.getWriter(); things start looking difficult. This 9 snippet of code runs in a fairly com- 10 try { plex environment (a Web server and a 11 int temp_f = Integer.parseInt(str_f); servlet container), and it requires a 12 double temp_c = (temp_f – 32) * 5.0 / 9.0; user sitting at a browser to interact 13 out.println(“Fahrenheit: “ + temp_f + with it. This is hardly the basis of a “, Celsius: “ + temp_c); 14 } good automated unit test. 15 catch (NumberFormatException e) { But let’s look at our servlet code 16 out.println(“Invalid temperature: “ + str_f); again. Its interface is pretty simple: as 17 } we mentioned before, it receives two 18 } parameters, a request and a response. The request object must be able to Figure 1. A trivial servlet that converts temperatures from provide a reasonable string when its Fahrenheit to Celsius. getParameter() method is called, and the response object must support I The real object has nondeterminis- We can use mock objects in two dis- setContentType() and getWriter(). tic behavior. tinct ways. First, we can use them to It’s starting to look as if we might be I The real object is difficult to set up. set up an environment in which our able to write some stubs: objects that I The real object has behavior that test code runs: we can initialize values pretend to be real request and response is hard to trigger (for example, a in the objects that the method under objects but that contain just enough network error). test uses. Figure 2 shows a typical set logic to let us run our code. In principle I The real object is slow. of tests using the JUnit testing frame- this is easy: both HttpServletRequest I The real object has (or is) a user work, which is available at www.junit. and HttpServletResponse are inter- interface. org. We use a MockHttpServletRe- faces, so all we have to do is whip up a I The test needs to ask the real ob- quest object to set up the context in couple of classes that implement the in- ject about how it was used (for ex- which to run the test. On line six of the terfaces and we’re set. Unfortunately, ample, a test might need to check code, we set the parameter “Fahren- when we look at the interface, we dis- to see that a callback function was heit” to the value “boo!” in the re- cover that we’ll need to implement actually called). quest object. This is equivalent to the dozens of methods just to get the thing I The real object does not yet exist. user entering “boo!” in the corre- to compile. Fortunately, other folks sponding form field; our mock object have already done the work for us. Mackinnon, Freeman, and Craig eliminates the need for human input also developed the code for a mock ob- when the test runs. Mock objects ject framework for Java programmers Mock objects can also verify that Tim Mackinnon, Steve Freeman, (available at www.mockobjects.com). actions were taken. On line seven of and Philip Craig introduced the con- Let’s use that code to test our servlet. Figure 2, we tell the response object cept of mock objects in their paper The good news is that in addition that we expect the method under test “Endo-Testing: Unit Testing with to the underlying framework code, the to set the response’s content type to Mock Objects” (www.cs.ualberta.ca/ mockobjects package comes with a text/html. Then, on lines 9 and 22, ~hoover/cmput401/XP-Notes/xp-conf/ number of mocked-up application-level after the method under test has run, Papers/4_4_MacKinnon.pdf), which objects. You’ll find mock output objects we tell the response object to verify they presented at XP2000. Their idea (OutputStream, PrintStream, and that this happened. Here, the mock is a natural extension of the ad hoc PrintWriter), objects that mock the object eliminates the need for a hu- stubbing that testers have been doing java.sql library, and classes for fak- man to check the result visually. This all along. The difference is that they ing out a servlet environment. In par- example shows a pretty trivial verifi- describe a framework to make writ- ticular, the package provides mocked- cation: in reality, mock objects can ing mock objects and incorporating up versions of HttpServletRequest verify that fairly complex sequences them into unit testing easier. and HttpServletResponse, which by of actions have been performed. Their paper lists seven good rea- an incredible coincidence are the types Mock objects can also record the sons to use a mock object (para- of the parameters of the method we data that was given to them. In our phrased slightly here): want to test. case, the response object receives the May/June 2002 IEEE SOFTWARE 23 SOFTWARE CONSTRUCTION 1 public void test_bad_parameter() throws Exception { hoc basis, coding up stubs when we 2 TemperatureServlet s = new TemperatureServlet(); needed them. However, we 3 MockHttpServletRequest request = personally have recently started new MockHttpServletRequest(); benefiting from adopting a more 4 MockHttpServletResponse response = systematic approach to creating new MockHttpServletResponse(); mock objects. Even things as simple 5 as consistent naming schemes have 6 request.setupAddParameter(“Fahrenheit”, “boo!”); helped make our tests more readable 7 response.setExpectedContentType(“text/html”); and the mock objects themselves more 8 s.doGet(request, response); portable from project to project. 9 response.verify(); There are several mock object frame- 10 assertEquals(“Invalid temperature: boo!\r\n”, works to choose from. Three for Java 11 response.getOutputStreamContents()); are at www.c2.com/cgi/wiki?MockOb- 12 } ject, and a fine implementation for Ruby 13 is at www.b13media.com/dev/ruby/ 14 public void test_boil() throws Exception { mock.html. If the thought of writing 15 TemperatureServlet s = new TemperatureServlet(); all the mock object classes you might 16 MockHttpServletRequest request = new MockHttpServletRequest(); need is intimidating, look at Easy- 17 MockHttpServletResponse response = Mock (www.easymock.org), a con- new MockHttpServletResponse(); venient Java API for creating mock 18 objects dynamically.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    3 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us