[TFUI] Mock The Server

"Phlip" <[email protected]> Thu, 22 Sep 2005 11:32:58 -0700
Newsgroups gmane.comp.programming.test-first-user-interfaces
Message-ID <000b01c5bfa4$effe0130$0100a8c0@Marley>
TFUI:

This pattern looks obvious, so why haven't I seen more of it?

To test-first the behavior of a Servlet or CGI web application, you ought to
cover the interaction between your server, URLs, and your application's
responses.

That sounds silly. An URL is just a protocol://server/applet?parameter=value
thing. These are designed to change easily; and defects will be obvious and 
easy to understand. You wouldn't, for example, test-first that a function 
takes certain simple arguments in a certain order; that's obvious.

URLs are part of your usability envelop. Suppose you have a Wiki, where the 
URLs change on the fly and must follow special rules. Now suppose your Wiki 
adapts to two different kinds of servers - servlet-style and CGI-style. So 
these two URLs hit the same page:

    http://localhost:8001/SamplePage
    http://localhost:8001/cgi-bin/wiki.cgi?SamplePage

Now suppose our Wiki can embed <a name="tag"> tags, so hitting an URI of 
/SamplePage#tag will jump the browser to that tag.

I want the Wiki to do something with the #tag, but it can't because web 
browsers snarf the tag before submitting the URL. The server never sees it.

So I want to upgrade the Wiki source to accept tags like this:

    http://localhost:8001/SamplePage!tag
    http://localhost:8001/cgi-bin/wiki.cgi?SamplePage!tag

A page will say <body onload="window.navigate('#tag');"> to jump there. 
Because the Wiki-side software can see the tag, it can do extra things like 
open test cases at that tag. This, in turn, allows you to link directly to 
test cases from within your wiki, like this: TestWikiFormat!internal_links.

So here goes. We use Webrick, a nifty little portable web server built for 
extensing. To handle each HTTP GET, you provide a function called 
do_GET(request, response). The request object connects to the GET parameters 
(including the URL), and the response contains a data bucket for you to fill 
with an outgoing page.

class MockHTTPRequest
 def initialize(nu_path)
  @path = nu_path
  @path_info = ''
  @script_name = ''
  @query_string = ''
 end

 attr_writer :path_info
 attr_reader :path, :path_info, :script_name, :query_string
end

I could have also used a dynamic Mock Object that mocks anything. To write 
that object, I just added empty members until the syntax errors went away. 
The Mock Object would have automagically done the same thing.

Now we create a Webrick server, switched to mock mode:

 def servePage(pageName)
  logger = WEBrick::Log.new(nil, WEBrick::Log::FATAL)

  server = WEBrick::HTTPServer.new(
       :Logger=>logger,
       :DoNotListen => true
       )

  server.mount '/', MiniSerlet, ''

  aServlet = MiniServlet.new(server,nil)

  request = MockHTTPRequest.new('/' + pageName)
  response = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP)

  aServer.do_GET(request, response)
  return response
 end

Note the :DoNotListen symbol. That tells the system this is just a Mock 
Server, not a real server. It only exists so our production MiniServlet can 
construct with it. Webrick's server has the ability to Mock built into it. A 
more advanced test could abuse that server with more aggressive scenarios. 
This test just uses that very convenient server to avoid writing another, 
more complex Mock object.

The servePage() fixture then creates a mock Request and a real HTTPResponse 
object. We pass the URI into the mock request, and call the target do_GET() 
method. That is the Activate step of the sequence Assemble, Activate, 
Assert.

Here's a test case to show we hit the front page correctly:

 def test_miniServerLikesTheFrontPage()
  response = servePage('FrontPage')
  assert_equal 200, response.status = 200
  assert_equal "text/html", response['Content-Type']
  assert_match /title.FrontPage..title/, response.body
 end

To test-first a !tag, we clone that test and change the test resource 
'FrontPage' to 'FrontPage!tag'. That will force us to edit the contents of 
do_GET() to accept the new URL.

Now we need to test CGI. Webrick can mount a CGI handler as one of its 
servlets. Here's the non-mock server doing it:

    server = WEBrick::HTTPServer.new(
    :ServerName => host,
    :Port => port,
    :CGIInterpreter => "C:/Ruby/bin/ruby.exe"  #  TODO  search for the ruby 
binary
    )

    server.mount '/cgi-bin', WEBrick::HTTPServlet::FileHandler, 'cgi-bin'
    server.mount '/', MiniServlet, ''

From there, it's a short step to mount the CGI handler on the Mock Server, 
and then re-use those tests (as the Abstract Test Pattern) to also hit the 
CGI version of our new URL.

This lecture has shown the principle "Just Another Object" in action. The 
server is just an object; it is not a special thing exempt from 
construction, extension, and destruction like other objects.

Unless your environment couples the servlets and server to each other and 
everything else, so mocking them is impossible...

-- 
  Phlip
  http://www.greencheese.org/ZeekLand  <-- NOT a blog!!! 



------------------------ Yahoo! Groups Sponsor --------------------~--> 
Fair play? Video games influencing politics. Click and talk back!
http://us.click.yahoo.com/T8sf5C/tzNLAA/TtwFAA/nhFolB/TM
--------------------------------------------------------------------~-> 

To unsubscribe, email:
TestFirstUserInterfaces-unsubscribe-hHKSG33TihhbjbujkaE4pw@public.gmane.org
 
Yahoo! Groups Links

<*> To visit your group on the web, go to:
    http://groups.yahoo.com/group/TestFirstUserInterfaces/

<*> To unsubscribe from this group, send an email to:
    TestFirstUserInterfaces-unsubscribe-hHKSG33TihhbjbujkaE4pw@public.gmane.org

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/