From: Steve F. <sm...@us...> - 2002-09-08 15:19:01
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv18399/doc/xdocs Modified Files: a_longer_example.xml Log Message: Reworked servlet tests. Realised that testNoEntries is testMissingRequest Added ExpectationValue for parameterName Extracted some instance variables in the test case Index: a_longer_example.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/a_longer_example.xml,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- a_longer_example.xml 2 Sep 2002 22:40:11 -0000 1.4 +++ a_longer_example.xml 8 Sep 2002 15:18:57 -0000 1.5 @@ -86,6 +86,13 @@ } }</programlisting> + <tip><title>Just do it</title> + <para> + We've just done something here that's worth noticing. We've just created an instance of + a servlet by calling <function>new</function>, rather than using a servlet container. + If we can do that, we can write stand-alone unit tests. + </para> + </tip> <para> Let's see if this test passes. It doesn't test much, not even what it says in its name, but we just want to see if the basic infrastructure works — and we haven't seen a green @@ -320,16 +327,197 @@ &greenbar; <para> - Now we're ready for our first acceptance test. + <comment status="todo">Now we're ready for our first acceptance test.</comment> </para> </section> <!-- We write our first implementation of the servlet --> - <!-- TODO --> - <section> - <title>accept a name and return a result from a hard-coded collection.</title> + <title>We accept a name and return a result from a hard-coded collection.</title> + + <para> + Decided to return a single value for a known name. + First thing to test is that we get the name parameter from the request. + We can check that the <emphasis>right</emphasis> parameter is requested, but also need to test that + any is requested at all. + Copy and rename the test, we'll fix it soon. + But, need to override getParameter(), how can tell it's been called? Use an ExpectationValue. + </para> + +<programlisting> +public void testFindOneAddress() throws ServletException, IOException { + final StringWriter page = new StringWriter(); + final ExpectationValue requestParameterName = new ExpectationValue("Request parameter name"); + + HttpServletRequest mockRequest = new NullHttpServletRequest() { + public String getMethod() { + return "GET"; + } + public String getProtocol() { + return "HTTP/1.1"; + } + + public String getParameter(String parameterName) { + requestParameterName.setActual(parameterName); + return "surname"; + } + }; + + HttpServletResponse mockResponse = new NullHttpServletResponse() { + private String contentType; + public void setContentType(String aContentType) { + assertEquals("Content type", "text/plain", aContentType); + contentType = aContentType; + } + + public PrintWriter getWriter() throws IOException { + assertNotNull("Should have content type", contentType); + return new PrintWriter(page); + } + }; + + requestParameterName.setExpected("name"); + AddressBookServlet servlet = new AddressBookServlet(); + servlet.service(mockRequest, mockResponse); + + requestParameterName.verify(); + assertEquals("Should be empty response", "surname@domain", page.toString().trim()); +} +</programlisting> + +<screen> +There was 1 failure: +1) testFindOneAddress(nostone.addressbook.AddressBookServletTest) +AssertionFailedError: Request parameter name did not receive the expected Value. +Expected: name +Received: Nothing + at com.mockobjects.AbstractExpectation.assertEquals(AbstractExpectation.java:39) + at com.mockobjects.ExpectationValue.verify(ExpectationValue.java:69) + at nostone.addressbook.AddressBookServletTest.testFindOneAddress(AddressBookServletTest.java:81) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at com.intellij.execution.e.bp.main(bp.java:0) + +FAILURES!!! +Tests run: 2, Failures: 1, Errors: 0</screen> + + &redbar; + + <para> + Fix this somehow. + </para> + + <programlisting> +public class AddressBookServlet extends HttpServlet { + public AddressBookServlet() { + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String name = request.getParameter("name"); + + response.setContentType("text/plain"); + if( name == null ) { + response.getWriter().println( "No address found" ); + } else { + response.getWriter().println("surname@domain"); + } + } +}</programlisting> + &greenbar; + + <para> + The response.getWriter is duplicated, let's refactor. + </para> + + <programlisting> +protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String name = request.getParameter("name"); + + response.setContentType("text/plain"); + response.getWriter().println( + name == null ? "No address found" : "surname@domain"); +}</programlisting> + &greenbar; + + <para> + Time to fix the tests. Pull lots of stuff into instance fields. + </para> + + <programlisting> +public class AddressBookServletTest extends TestCase { + private final StringWriter page = new StringWriter(); + private final ExpectationValue requestParameterName = new ExpectationValue("Request parameter name"); + private HttpServletResponse mockResponse = new NullHttpServletResponse() { + private String contentType; + public void setContentType(String aContentType) { + assertEquals("Content type", "text/plain", aContentType); + contentType = aContentType; + } + + public PrintWriter getWriter() throws IOException { + assertNotNull("Should have content type", contentType); + return new PrintWriter(page); + } + }; + private AddressBookServlet servlet = new AddressBookServlet(); + + public AddressBookServletTest(String name) { + super(name); + } + + public void testNoEntries() throws ServletException, IOException { + HttpServletRequest mockRequest = new NullHttpServletRequest() { + public String getMethod() { + return "GET"; + } + public String getProtocol() { + return "HTTP/1.1"; + } + }; + servlet.service(mockRequest, mockResponse); + + assertEquals("Should be empty response", "No address found", page.toString().trim()); + } + + public void testFindOneAddress() throws ServletException, IOException { + requestParameterName.setExpected("name"); + HttpServletRequest mockRequest = new NullHttpServletRequest() { + public String getMethod() { + return "GET"; + } + public String getProtocol() { + return "HTTP/1.1"; + } + + public String getParameter(String parameterName) { + requestParameterName.setActual(parameterName); + return "surname"; + } + }; + servlet.service(mockRequest, mockResponse); + + requestParameterName.verify(); + assertEquals("Should be empty response", "surname@domain", page.toString().trim()); + } +}</programlisting> + + <para> + We couldn't reuse mockrequest as it stands because the getParameter() method is different. + Note in this task, testNoEntries is now the wrong thing to test, because we are going to search for names + in a hard-coded address book, and so we will never have no entries. Instead, we want to test searching for + a name that is not in the address book. So testNoEntries should really be testNoAddressFound. + However, at it stands, testNoEntries is actually testing how the servlet handles a request with no name + parameter. This is a useful -- we can reuse the test as it stands to test our servlet's error handing. + A useful clue is that we're not using "surname", the value of the name parameter. + So, the test is driving us to add the book logic to the servlet. + </para> </section> <!-- accept a name and return a result from a hard-coded collection --> + <!-- TODO --> + <section> <title> Retrieve the entries from a file, specified as a servlet property. @@ -356,7 +544,7 @@ Sort the results alphabetically </para></listitem> </itemizedlist> - </chapter> + </chapter> <!-- List all the entries in the book --> <chapter> <title>Add a name and address</title> @@ -372,10 +560,11 @@ lock the file while it's being written. </para></listitem> </itemizedlist> - </chapter> + </chapter> <!-- Add a name and address --> </part> +<!-- <part status="todo"> <title>Iteration 2</title> @@ -427,4 +616,5 @@ Click on name to see its entries. </para> </chapter> -</part> \ No newline at end of file +</part> +--> \ No newline at end of file |