The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
\chapter{Testing}
\label{chap:testing}

When bigtop builds your application, it makes four test files.  These are
kept up to date on each rebuild, unless you turn them off in some way.  This
chapter explains their purposes and uses, then goes on to explain how to
use one of them as a model for building your own more ellaborate tests.

\section{Standard Tests by bigtop}

The Control backends are responsible for building tests to go with their
controllers.  The Control Gantry backend builds and maintains four tests
in the t subdirectory of the build directory:

\begin{tabular}{l|l}
Test Name & Purpose \\
\hline
\verb+01_use.t+      & one \verb+use_ok+ test for each controller stub      \\
\verb+02_pod.t+      & standard Test::Pod \verb+all_pod_files_ok+           \\
\verb+03_podcover.t+ & standard Test::Pod::Coverage \verb+all_pod_files_ok+ \\
\verb+10_run.t+      & default page request for each controller             \\
\end{tabular}

These test files are purposely spartan.  You are not meant to edit them.
To run them, build in the usual way:

\begin{verbatim}
perl Build.PL
./Build
./Build test
\end{verbatim}

Once you do that, you can usually re-run the tests with just the last step.

There is nothing particularly interesting about the \verb+use_ok+ tests,
they merely check compilation of each controller module.  The pod and
pod coverage tests are even less interesting.  To use them, you need to
install Test::Pod and Test::Pod::Coverage and set the \verb+TEST_POD+
environment variable in your shell.

The run tests are slightly more interesting.  There is one test for
each controller in the application, plus one for the base module.  Each
test is a simple page hit on the \verb+do_main+ method, which only checks
for successful return status.  Yet, these can still serve as an example
for your own tests.  So, let's look at them in more detail.

\section{Run Tests}

To explain how to build specific tests, I'll start by walking you through
the tests bigtop makes by default, using \verb+t/10_run.t+ from the
address book example in Chapter \ref{chap:simpleex} in particular.  I'll
show a little bit of the script at a time with comments interspersed.
To see the whole thing simply look at the \verb+t/10_run.t+ bigtop makes
when you type:

\begin{verbatim}
bigtop -n AddressBook address
\end{verbatim}

The real version has a bit more in it.  I've distilled it to show what
you need to do for your own tests, avoiding issues like the SKIP block
which prevents default tests from running if you don't have an SQLite
database for them to use.

The top of the script is typical of any test which relies on Tests::More:

\begin{verbatim}
use strict;

use Test::More tests => 2;

use AddressBook qw{ -Engine=CGI -TemplateEngine=TT };

use Gantry::Server;
use Gantry::Engine::CGI;
\end{verbatim}

It uses the base module of the application, specifying the CGI and TT
engines.  Then it brings in Gantry::Server to drive the tests and the
CGI engine.  The last is used mostly for documentation, since Gantry will
require it based on the import list to AddressBook.

\begin{verbatim}
my $cgi = Gantry::Engine::CGI->new( {
    config => {
        dbconn => 'dbi:SQLite:dbname=app.db',
        template_wrapper => 'genwrapper.tt',
        root => 'html',
    },
    locations => {
        '/' => 'AddressBook',
        '/address' => 'AddressBook::Address',
    },
} );
\end{verbatim}

For testing, we need a CGI engine object.  The configuration here is minimal,
containing only the database DSN string, Template Toolkit wrapper file name,
and trivial root template path.  Recall that the original address book
had only one table called address.  So, there are two locations here: the
base location and one for the sole table.  The locations hash key
stores URL/controller pairs.  The URLs are absolute from the document
root of the server.

\begin{verbatim}
my @tests = qw(
    /
    /address
);
\end{verbatim}

The generated test script will hit each location.  When you write your
own tests, there is no need to repeat these hits on each controller's default
URL.  See Section \ref{sec:customtests} below for how to make more
interesting hits.

\begin{verbatim}
my $server = Gantry::Server->new();
$server->set_engine_object( $cgi );
\end{verbatim}

We need to instantiate a Gantry::Server, which is really an
HTTP::Server::Simple object.  To avoid constructor overloading, Gantry::Server
provides \verb+set_engine_object+.  We pass it the CGI engine object
built earlier.

\begin{verbatim}
foreach my $location ( @tests ) {
    my( $status, $page ) = $server->handle_request_test( $location );
    ok( $status eq '200',
            "expected 200, received $status for $location" );

    if ( $status ne '200' ) {
        print STDERR $page . "\n\n";
    }
}
\end{verbatim}

Gantry::Server provides \verb+handle_request_test+ for testing.  It simulates
a single page request in the application and returns the result.  The
default tests only check the return status.  You could scrape the page
if you so desire.  You could also directly query the database from the
test to see if a particular hit went as expected.  For that you would need
to provide data to the page.  Keep reading.

There are two ways to feed data to a page.  Some pages expect to process
forms, \verb+handle_request_test+ is ready to help them.  It takes anything
from the query string and feeds it as if it were in the body of a POST
request.  For example, suppose you have a controller with location
\verb+/search+, which expects a form parameter called searchstr:

\begin{verbatim}
my @tests = qw(
    /search?searchstr=keyword
);
\end{verbatim}

To understand how to test the rest of the page types, we need to take
a step back and discuss Gantry dispatching.

\section{Gantry Dispatching}

Dispatching is the process of turning a browser requested URL into a method
call.  Gantry has a simple scheme, but there are some subtlies.  

The basic scheme is straightforward.  Whether you deliver the application
with \verb+mod_perl+, CGI, FastCGI, or the stand alone server, you must
specify a list of locations and the controllers which respond to them.  If
the browser requests one of those locations, Gantry will call the
\verb+do_main+ method in the associated controller.

What happens when the requested location is not listed exactly as a location
is more interesting.  Work begins with the full URL.  If that matches
a location, we would be in the basic scheme.  So, one at a time path elements
are removed from the tail of the URL, until what remains is a location from
the list.   The web server does this.  It then passes the remainder of
the URL to Gantry's handler, which uses the first element as the method name
and the remainder as parameters for that method.  Before calling the method,
Gantry prepends \verb+do_+ to the name.  This makes it a bit harder for
URL spoofing to hit methods unexpectedly, but mostly serves as clear
documentation of which methods in a controller are externally visible.

Here's an example.  Suppose that someone requests \verb+/address/meth/a/b+
on our example address book.  The web server (Apache or the stand alone
server) looks for the longest location prefix it can find in its location
list (it finds \verb+/address+) and hands the request to the \verb+handler+
method of the registered module for that location (AddressBook::Address is
registered for \verb+/address+).  The \verb+handler+ method is inherited from
Gantry.pm, which uses the remainder of the url to decide what method to
call.  If nothing were left, it would call \verb+do_main+.  In this case,
\verb+meth/a/b+ remains, so it will call \verb+do_meth+, passing it
\verb+a+ and \verb+b+ as arguments.

\section{Custom Tests}
\label{sec:customtests}

Now that we understand the dispatching scheme explained in the previous
section, it is a small step to creating tests of arbitrary \verb+do_+
methods with parameters and/or form data.

If the method in question is expecting data from the URL, there is not
much to the test.  Simply add it to the URL in the \verb+@tests+ list.
For example:

\begin{verbatim}
my @tests = qw(
    /address/stubby/a/b/c
);
\end{verbatim}

For the current app, this will test a hit to the \verb+do_stubby+ subroutine,
which will receive \verb+a+, \verb+b+, and \verb+c+ as parameters.

%If the method expected its parameters in the query string, we could have said:
%
%\begin{verbatim}
%my @tests = qw(
%    /address/stubby?var=val&var2=val2
%);
%\end{verbatim}

The only thing clever is form submission.  As mentioned above, to post
form parameters, just put them in the query string.  The
\verb+handle_request_test+ method of the Gantry::Server takes the query
string parameters and passes them as if they were POSTed.

Now that you know how to simulate page hits, you can help yourself to the
various Test:: modules on CPAN to complete your custom testing program.