[A repost from the TFUI mailing list]

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!!!