Re: CGI testing with a mock object in Perl

Phlip <[email protected]>
Newsgroups gmane.comp.programming.test-driven-development,gmane.comp.programming.test-first-user-interfaces
Message-ID <[email protected]>
Rick Reynolds wrote:

> <some instruction and example code snipped>

I converted that sample code into bookyuck:

----8<-------------------

    Bootstrapping CGI in Perl
Each TFUI development effort begins in a tricky spot.
Your project must learn to use test-first, and not its
wizard or its Web server, typically without support
from your GUI Toolkit's documentation.

This sample script squeezes two modules together - a
GuiLayer, and its test, called main: 

    #!/usr/bin/env perl -w
    
    use strict;  #  don't leave home without it
    use CGI;    
    use Test::Unit::TestRunner;
    use XML::XPath;
    
    package GuiLayer;
    
        sub processRequest
        {
            my $self = shift;
            my $cgi = shift;
            
            my $payload = $cgi->param("payload");
        
            return $cgi->html(
               $cgi->body("This page says $payload")
                         );
        }

    package main;
        use base qw(Test::Unit::TestCase);
        
        sub test_verify_page_contents {
            my $self = shift;
            
# make CGI think a Web browser called it to build a 
# page
            
            $ENV{'REQUEST_METHOD'} = 'GET';
      $ENV{'QUERY_STRING'} = 'payload=Daddy+Warbucks';
            my $cgi = CGI->new();
            
        # build the page
            
       my $response = GuiLayer->processRequest($cgi);
            
        #  parse the page's contents
            
            my $xp = XML::XPath->new(xml =>
$response);
            my $node = $xp->find('/html/body/text()');
            
        #  check our payload influenced the page
            
     $self->assert_matches(qr/Daddy Warbucks/, $node);
        }

    my $test = Test::Unit::TestRunner->new;
    $test->start('main');  #  Test Collector pattern

The test makes $cgi and GuiLayer think a Web browser
requested its page, obeying an URL such as:

http://localhost/cgi-bin/whatever.pl?payload=Daddy+Warbucks

The test parses the HTML with our favorite utility,
XPath, and asserts our sample string appeared inside
it. If your QUERY_STRING must contain a more complex
URL, remember to use URLEncode() to escape any
non-ASCII parameters. I fudged when I represented a
space as a +.

GuiLayer does not depend on a Web server-only $cgi. If
GuiLayer used any lower modules, the ones containing
business logic would naturally avoid $cgi.

(Thanks to Tony Byrne, on the TDD mailing list, for
seeding this topic.)

    Temporary HTML Visual Inspections
Write a test that samples some XHTML from your site,
copies it to a file called "test.html", and commands
your Web browser to display that file:

            open(TEMP, '>c:/temp/temp.html');
            print TEMP $response;
            close(TEMP);
            system('start c:/temp/temp.html');

"start" depends on your MS Windows CMD.EXE, so you may
need some other system to view the page, such as
"konqueror".


----8<-------------------

> I agree.  I was starting to come to the very same
> kind of conclusion myself
> when your message came through.  What has been
> hindering me is that the code
> in this app is all very procedural.  Various
> routines (such as
> "VerifyUserHasLoggedIn") will just blindly print to
> the output if they hit
> an error condition, and won't return any error
> codes.  This, of course,
> makes things hard to test. 

The joy of all of HTTP, XHTML, HTML, etc. is the tool
support for parsing. If you had the "noodle strands
going everywhere problem", but no standard protocols
that force everyone to line up and fit in, then you
would be extra sunk.

When you refactor, you want a test failure to stop any
sloppiness, whether or not you made a mistake that
broke logic. So instead of testing logic, test the
HTML, in bulk. 

Start at the WWW::Mechanize package, and write a whole
bunch of completely stoopid non-test-first tests. Each
will hit your server, download pages, and then
critically examine them. Run them thru Tidy (and use
Perl to throw away many of the impossible errors).

In some circumstances, simply save the downloaded
pages as files, and compare them for direct equality.
No Perly regexps. Schedule these tests for future
retirement. Automate their sunset if necessary.

The point of these tests is not to lead directly to
test-first. The point is to fail instantly and
savagely if your refactoring makes a mistake.

> I'm beginning to feel
> that I need to bite the
> bullet and refactor a bunch of those library
> routines so that they have a
> printing wrapper around another piece of code that
> does the actual checking.
> My work can call the checking code directly while
> the rest of the app chugs
> along nicely calling the printing wrapper.

Call a test whose failure detects a sloppy refactor
"hyperactive". Such tests might fail even if the user
would not perceive a bug. TDD, done right from
scratch, can run a very high level of latent
hyperactivity, and you'l never notice. 

To get ready for the refactor party, we just want
pre-emptive hyperactivity, without even constraining
anything. Only a few siesmographs are needed to detect
earthquakes.

Configure your editor so its One Test Button runs
tests on the GGI layer (aka HTML Tests), and also on
the HTTP layer. Put together sufficient shell scripts
that ensure each code change deploys to the server.

Then make a few sloppy refactors, and watch the sparks
fly.

> Yes.  The problem is that I don't know how much
> longer I'll end up working
> in this code base.  If I knew that I was being
> shifted into the role of
> primary maintainer of this code I would feel that
> any amount of refactoring
> work would be worth it.  But I may just need to
> implement a feature and then
> move away from the code onto something else (I
> wasn't really hired to do the
> work they've given to me at the moment -- I just
> happen to be one of the
> best fits for the job at this time).

The next phase in my schedule is normal TDD and light
refactoring on the lower levels. To maintain, not
develop, simply follow the rule "how can I avoid ever
having to look at this function again?"


=====
Phlip
  http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces


		
__________________________________
Do you Yahoo!?
Yahoo! Mail Address AutoComplete - You start. We finish.
http://promotions.yahoo.com/new_mail 


------------------------ Yahoo! Groups Sponsor --------------------~--> 
Make a clean sweep of pop-up ads. Yahoo! Companion Toolbar.
Now with Pop-Up Blocker. Get it for free!
http://us.click.yahoo.com/L5YrjA/eSIIAA/yQLSAA/NhFolB/TM
--------------------------------------------------------------------~-> 

 
Yahoo! Groups Links

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

<*> To unsubscribe from this group, send an email to:
    [email protected]

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.