Testing mod_perl 2.0
by Geoffrey YoungMay 22, 2003
Last time,
we looked at writing a simple Apache output filter -
Apache::Clean -
using the mod_perl 2.0 API. How did I know that the filter
I presented really worked?
I wrote a test suite for it, one that exercised
the code
against a live Apache server
using the Apache-Test testing framework.
Writing a series of tests that executes against a live
Apache server has become much simpler since the advent of
Apache-Test. Although Apache-Test,
as part of the
Apache HTTP Test Project,
is generic enough to
be used with virtually any version of Apache (with or without
mod_perl enabled), it comes bundled with mod_perl 2.0, making
it the tool of choice for writing tests for your
mod_perl 2.0 modules.
Testing, Testing, 1, 2, 3
There are many advantages to writing tests.
For instance, maintaining the test suite as I coded Apache::Clean
allowed me to test each functional unit as I implemented it,
which made development easier. The individual tests
also allowed me to be fairly certain that the module would
behave as expected once distributed. As an added bonus,
tests offer additional end-user documentation in the form of
test scripts, supporting libraries and configuration files,
available to anyone who wants to snoop around the distribution
a bit. All in all, having a test suite increases the value of your
code exponentially, while at the same time making your life
easier.
Of course, these benefits come from having any
testing environment, and are not limited to just Apache-Test.
The particular advantage that Apache-Test
brings to the table is the ease at which it puts a whole,
pristine, and isolated Apache server at your disposal,
allowing you to test and exercise
your code in a live environment with a minimum
of effort. No more Apache::FakeRequest, no
more httpd.conf configurations strewn across
development environments or corrupted with proof-of-concept
handlers that keep you busy following non-bugs for half a day.
No more mess, no more tears.
If you have ever used tools like Test.pm
or Test::More as the basis for testing your modules, then
you already know most of what using Apache-Test
is going to look like. In fact, Apache-Test
uses Test.pm under the hood, so the layout
and syntax are similar. If you have never written
a test before, (and shame on you)then
An
Introduction to Testing provides a nice
overview of testing with Perl. For the most part, though,
Apache-Test
is really simple enough that you should be able to follow
along here without any trouble or previous knowledge.
Leveraging the Apache-Test framework requires
only a few steps - generating the test harness, configuring
Apache to your specific needs, and writing the tests -
each of which is relatively straightforward.
Generating the Test Harness
The first step to using Apache-Test
is to tweak the Makefile.PL for your
module. If you don't yet have a Makefile.PL,
or are not familiar with how to generate one, then don't worry - all that is required is a simple call to
h2xs, which provides us with a standard
platform both for distributing our module and
deploying the Apache-Test infrastructure.
$ h2xs -AXPn Apache::Clean
Defaulting to backward compatibility with perl 5.9.0
If you intend this module to be compatible with earlier perl versions, then please
specify a minimum perl version with the -b option.
Writing Apache/Clean/Clean.pm
Writing Apache/Clean/Makefile.PL
Writing Apache/Clean/README
Writing Apache/Clean/t/1.t
Writing Apache/Clean/Changes
Writing Apache/Clean/MANIFEST
h2xs generates the necessary structure
for our module, namely the Clean.pm template
and the Makefile.PL, as well as the
t/ subdirectory where our tests and supporting
files will eventually live. You can take some extra
steps and shuffle the distribution around a bit (such
as removing t/1.t and putting everything
into Apache-Clean/ instead of
Apache/Clean/) but it is not required.
Once you have the module layout sorted out and
have replaced the generated Clean.pm stub with
the
actual Clean.pm filter from before,
it's time to start preparing the basic test harness.
To begin, we need to modify the Makefile.PL significantly.
The end result should look something like:
#!perl
use 5.008;
use Apache2 ();
use ModPerl::MM ();
use Apache::TestMM qw(test clean);
use Apache::TestRunPerl ();
# configure tests based on incoming arguments
Apache::TestMM::filter_args();
# provide the test harness
Apache::TestRunPerl->generate_script();
# now, write out the Makefile
ModPerl::MM::WriteMakefile(
NAME => 'Apache::Clean',
VERSION => '2.0',
PREREQ_PM => { HTML::Clean => 0.8,
mod_perl => 1.9909, },
);
|
Related Reading
|
Let's take a moment to analyze our nonstandard Makefile.PL. We begin by importing
a few new mod_perl 2.0 libraries. The first
is Apache2.pm. In order to peacefully
co-exist with mod_perl 1.0 installations, mod_perl 2.0
gives you the option of installing mod_perl relative
to Apache2/ in your @INC,
as to avoid collisions with 1.0 modules of the same name.
For instance,
the mod_perl 2.0 Apache::Filter we used
to write our output filter interface
would be installed as Apache2/Apache/Filter.pm.
Of course, ordinary calls that require() or
use() Apache::Filter in mod_perl 2.0 code
would fail to find
the correct version (if one was found at all), since
it was installed in a nonstandard place.
Apache2.pm extends @INC to
include any (existing) Apache2/ directories
so that use() and related statements work as intended.
In our case, we need to use() Apache2 in
order to ensure that, no matter how the end-user configured
his mod_perl 2.0 installation, we can find the rest of the
libraries we need.
Secure in the knowledge that our Makefile.PL
will be able to find all our other mod_perl 2.0
packages (wherever they live), we can proceed.
ModPerl::MM provides the WriteMakefile()
function, which is similar to the ExtUtils::MakeMaker
function of the same name and takes the same options.
The reason that you will want to use the
WriteMakefile() from ModPerl::MM is that,
through means highly magical, all of your
mod_perl-specific needs are satisfied.
For instance, your module will be installed relative to
Apache/ or Apache2/, depending
on how mod_perl itself is installed. Other nice features
are automatic inclusion of mod_perl's typemap
and the header files required for XS-based modules, as well as magical
cross-platform compatibility for Win32 compilation, which has been
troublesome in the past.
Keep in mind that neither Apache2.pm nor
ModPerl::MM are required in order to use
Apache-Test -
both are packages specific to mod_perl 2.0 and any
handlers you may write for this version (as will be touched
on later, Apache-Test can be used for
mod_perl 1.0 based modules as well, or even
Apache 1.3 or 2.0 modules independent of mod_perl,
for that matter). The next package, Apache::TestMM,
is where the real interface for Apache-Test
begins.
Apache::TestMM, contains
the functions we will need to configure the test harness.
The first thing we do is import the test()
and clean() functions, which generate their respective
Makefile targets so that we can run
(and re-run) our tests.
After that, we call the filter_args() function. This allows
us to configure various parts of our tests on the command line
using different options, which will be discussed later.
The final part of our configuration uses the
generate_script() method
from the Apache::TestRunPerl class, which writes
out the script responsible
for running our tests, t/TEST.
It is t/TEST that will be invoked
when a user issues make test, although
the script can be called directly as well.
While t/TEST can end up containing lots of
information, if you crack it open, then you would see that the
engine that really drives the test suite is rather simple.
use Apache::TestRunPerl ();
Apache::TestRunPerl->new->run(@ARGV);
Believe it or not, the single call to run() does all
intricate work of starting, configuring, and stopping Apache,
as well as running the individual tests we (still) have
yet to define.
Despite the long explanations, the net result of our activity
thus far has been a few modifications to a typical
Makefile.PL so that
it reflects the needs of both our mod_perl 2.0 module and
our forthcoming use of the Apache-Test infrastructure.
Next, we need to configure Apache for the tests
specific to the functionality in our handler.
Configuring Apache
Ordinarily, there are many
things you need to stuff into httpd.conf
in order to get the server responding to requests, only
some of which are related to the content the
server will provide.
The Apache-Test framework provides a minimal
Apache configuration, such as default DocumentRoot,
ErrorLog, Listen, and other settings
required for normal operation of the server. In fact, with no
intervention on your part, Apache-Test
provides a configuration that enables you to successfully request
/index.html from the server. Chances are,
though, that you will need something above a basic
configuration in order to test your module appropriately.
To add additional settings to the defaults, we create
the file t/conf/extra.conf.in, adding
any required directories along the way. If
Apache-Test sees extra.conf.in, then
it would pull the file into its default configuration
using an Include directive (after some
manipulations we will discuss shortly). This provides
a nice way of adding only the configuration data you
require for your tests, and saves you from the need to
worry about the mundane aspects of running the server.
One of the first aspects of Apache::Clean
we should test is whether it can clean up a simple,
static HTML file. So, we begin our extra.conf.in
with the following:
PerlSwitches -w
Alias /level @DocumentRoot@
<Location /level>
PerlOutputFilterHandler Apache::Clean
PerlSetVar CleanLevel 2
</Location>
This activates our output filter for requests to
/level. Note the introduction of a new
directive, PerlSwitches, which allows you to pass
command line switches to the embedded perl interpreter.
Here, we use it to enable warnings, similar to the
way that PerlWarn worked in mod_perl 1.0.
PerlSwitches can actually take
any perl command line switch, which makes it a fairly
useful and flexible tool. For example, we could use the -I
switch to extend @INC in place of
adding use lib statements to a
startup.pl, or use -T
to enable taint mode in place of the former
PerlTaintMode directive, which
is not part of mod_perl 2.0.
Next, we come to the familiar Alias
directive, albeit with a twist.
As previously mentioned, Apache-Test
configures several defaults, including DocumentRoot
and ServerRoot.
One of the nice features of Apache-Test
is that it keeps track of its defaults for you and provides
some helpful variable expansions. In my particular case,
the @DocumentRoot@ variable in the
Alias directive is replaced
with the value of the default DocumentRoot
that Apache-Test calculated for my build.
The real configuration ends up looking like
Alias /level /src/perl.com/Apache-Clean-2.0/t/htdocs
when the tests are run. This is handy, especially when you take into consideration that your tests may run on different platforms.
The rest of the configuration closely resembles our example
from last time - using the
PerlOutputFilterHandler to specify
Apache::Clean as our output filter, and
PerlSetVar to specify the specific
HTML::Clean level. The only thing missing
before we have prepared our module enough to run our
first test is some testable content in DocumentRoot.
As you can see from the @DocumentRoot@
expansion in the previous example, DocumentRoot
resolves to ServerRoot/t/htdocs/,
so that is one place where we can put any documents we are
interested in retrieving for our tests. So, we create
t/htdocs/index.html and place some
useful content in it.
<i ><strong>"This is a test"</strong></i >
Our index.html contains a number of
different elements that HTML::Clean can tidy,
making it useful for testing various configurations of Apache::Clean.
Now we have all the Apache configuration that is required:
some custom configuration directives in t/conf/extra.conf.in
and some useful content in t/htdocs/index.html. All that
is left to do is write the tests.

