Day 7 - Testing with an External Web Server
Adding built in tests for an external web server to your application.
The Problem
In some cases you might want your Catalyst application to be deployable in a number of different situations. To be comprehensive you need to build a test environment that will test your deployment in each environment. Here's how to do this so that you can test simultaneously with the built in server, Apache and Lighttpd, and includes how to deploy from the root of the web server or a sub directory.
Catalyst Testing
Essentially the aim of testing is to assess that your application
behaves in the expected manner. Catalyst::Test and
Test::WWW::Mechanize::Catalyst provides the methods to test your
catalyst applications. These are based on lwp and
Test::WWW::Mechanize respectively. Bu default these two Catalyst
test modules use the catalyst dispatcher directly to test your
application. This way you avoid having to use an server process
during development. However you can also use an external server to
run your tests as well by setting the CATALYST_SERVER
environment
variable. For example :
$ script/myapp_server.pl [ snipped output] You can connect to your server at http://zaphod.local:3000
And then from another terminal I do the following in the application root directory:
$ CATALYST_SERVER='http://localhost:3000/' prove -r -l lib/ t/
This will run the tests in the t/ directory using the built in server instance running on port 3000. The same can be done for an external deployment, for example the following:
$ $ CATALYST_SERVER='http://www.example.com/myapp' prove -r -l lib/ t/
And you run your tests on the external server.
Another approach is to automatically configure a catalyst application running on an external server your local computer. The rest of this entry explores this.
Automating testing with a local lighttpd installation
In the t/
directory of your application, you can place a script:
optional_lighttpd-fastcgi-non-root.pl
which contains the following
code:
#!/usr/bin/perl use strict; use warnings; use File::Path; use File::Slurp qw(write_file); use FindBin; use IO::Socket; use Test::More; eval "use File::Copy::Recursive"; plan skip_all => 'File::Copy::Recursive required' if $@; my $lighttpd_bin = $ENV{LIGHTTPD_BIN} || 'lighttpd'; plan skip_all => 'Cannot find lighttpd, please set LIGHTTPD_BIN' unless -x $lighttpd_bin; plan tests => 1;
Place any application specific configuration you need here, to for example create a version of your configuration which suits a testing environment (i.e. you can nuke the tests after the installation).
Next, set up a local configuration for lighttpd:
my $docroot = "$FindBin::Bin/../t/tmp"; my $port = 8529; # Clean up docroot path $docroot =~ s{/t/..}{}; my $conf = qq{ # basic lighttpd config file for testing fcgi+catalyst server.modules = ( "mod_access", "mod_fastcgi", "mod_accesslog" ) server.document-root = "$docroot" server.errorlog = "$docroot/error.log" accesslog.filename = "$docroot/access.log" server.bind = "127.0.0.1" server.port = $port # catalyst app specific fcgi setup fastcgi.server = ( "" => ( "FastCgiTest" => ( "socket" => "$docroot/test.socket", "check-local" => "disable", "bin-path" => "$docroot/lib/MyApp/myapp/myapp_fastcgi.pl", "min-procs" => 1, "max-procs" => 1, "idle-timeout" => 20 ) ) ) }; write_file "$docroot/lighttpd.conf", $conf;
Code to start this configuration of lighttpd:
my $pid = open my $lighttpd, "$lighttpd_bin -D -f $docroot/lighttpd.conf 2> &1 |" or die "Unable to spawn lighttpd: $!"; # wait for it to start print "Waiting for server to start...\n"; while ( check_port( 'localhost', $port ) != 1 ) { sleep 1; }
And finally code to run the tests:
$ENV{CATALYST_SERVER} = "http://localhost:$port"; system( 'prove -r -l t' );
The final part of the script shuts down the local server and cleans up the local configuration
# shut it down kill 'INT', $pid; close $lighttpd; # clean up rmtree "$FindBin::Bin/../t/tmp" if -d "$FindBin::Bin/../t/tmp"; ok( 'done' );
Deploying on a server subdirectory:
To make a configuration that deploys on a subdirectory, instead of this in the lighttpd config:
fastcgi.server = ( "" => ( "FastCgiTest" => (
We replace it with this:
fastcgi.server = ( "/myapp" => ( "FastCgiTest" => (
And replace this:
$ENV{CATALYST_SERVER} = "http://localhost:$port";
with this:
$ENV{CATALYST_SERVER} = "http://localhost:$port/myapp";
Of course you can do both. I have one script which is
optional_lighttpd-fastcgi.pl
and another which is
optional_lighttpd-fastcgi-non-root.pl
.
Running the tests
To run these tests just issue the following command from the application root:
$ perl -ilib t/optional_lighttpd-fastcgi.pl
Apache
Apache is a little more complicated in some ways. This time we use the Apache::Test module to help us build a local instance of the installed apache. This is because Apache::Test automatically discovers information about your local Apache installation which is a complicated job to do manually. It requires creating a few more files and directories than the lighttpd test harness. It seems that the configuration for root and non-root deployments needs to be in the same config file due to limitations with Apache::Test (please correct me if I'm wrong here).
Here's start of the optional_apache-fastcgi.pl
script:
#!/usr/bin/perl use strict; use warnings; use Apache::Test; use Apache::TestRun (); use File::Path; use File::Copy::Recursive; use FindBin; use IO::Socket; # clean up warn "cleaning up last test run\n"; rmtree "$FindBin::Bin/../t/tmp" if -d "$FindBin::Bin/../t/tmp";
And again here is where you want to tweak the configuration of the application to reflect the test environment.
$ENV{CATALYST_SERVER} = 'http://localhost:8529/'; Apache::TestRun->new->run(@ARGV);
In fact this test is based on the code from http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Runtime/t/optional_apache-fastcgi.pl which shows the complete set-up tear down implementation of this.
To configure the apache installation we need to provide a file called extra.conf in the directory t/conf. That will look something like this:
<IfModule mod_fastcgi.c> FastCgiIpcDir /path/to/MyApp/t/tmp/tmp FastCgiServer /path/to/MyApp/script/myapp_fastcgi.pl -idle-timeout 300 -processes 1 ScriptAlias / /path/to/MyApp/t/tmp/lib/MyApp/script/myapp_fastcgi.pl/ ScriptAlias /fastcgi/ /path/to/MyApp/script/myapp_fastcgi.pl/ </IfModule>
for deployment on both root and non-root locations. Tweak the value of $ENV{CATALYST_SERVER} to reflect the one you want to test, or indeed create a separate test script as for lighttpd.
We run the tests in the same way as for lighttpd.
wrap up
Here we have shown how it is fairly easy to test for a wide variety of deployment scenarios using the tools that Catalyst and CPAN provide. Of course it would be fairly easy to turn this into a Catalyst::Helper which may well be the next step for this project.
AUTHOR
Kieren Diment <diment@gmail.com>
COPYRIGHT.
Copyright 2006 Kieren Diment. This document can be freely redistributed and can be modified and re-distributed under the same conditions as Perl itself.