Reputation: 25445
I'm building a perl application to archive simple web pages (i.e. static pages with no query strings involved). I'd like to write tests to verify the functionality of the module that will be accessing the remote files. To make the tests self-reliant, I'm looking for a simple, self-contained web server that the test scripts can use locally.
Below is an example which outlines what I'm trying to do. I've cut it down to a minimum with the following directory structure:
./MirrorPage.pm
./t/001_get_url.t
./t/test-docroot/test-1.json
Contents of "./MirrorPage.pm":
package MirrorPage;
use Moose;
use LWP::Simple;
use namespace::autoclean;
sub get_url {
my ($self, $url_to_get) = @_;
### grab the contents of the url
my $url_data = get($url_to_get);
### return the contents.
return $url_data;
}
__PACKAGE__->meta->make_immutable;
1;
Contents of "./t/001_get_url.t":
#!/usr/bin/perl
use Modern::Perl;
use Test::More;
use MirrorPage;
### Start test www server on port 8123 here ###
my $t = new_ok('MirrorPage', undef, 'Create MirrorPage');
is(
$t->get_url("http://localhost:8123/test-1.json"),
'{ testkey: "testvalue" }',
"Verify the data."
);
### Kill test www server here ###
done_testing();
Contents of "./t/test-docroot/test-1.json":
{ testkey: "testvalue" }
The goal is to start and kill a self-contained web server at the corresponding comment locations in "./t/001_get_url.t". The web server needs to serve the contents of the "./t/test-docroot" directory as its document root.
Given all that: What is the best/simplest way to setup a self-contained web server to provide static files for testing in perl?
Upvotes: 4
Views: 1480
Reputation: 3382
I can highly recommend Mojolicious as a great way to produce a test server for a client. Since Mojolicious simply maps a URL into a subroutine call, it's very easy to have very fine control over what the server does, and therefore you can easily test things like "does my client fail properly if the server returns a bad response/bad content/times out". And since it is very simple to set up and tear down a server, a little cleverness with fork() makes it possible to have the test and the server setup live in the same test file.
Upvotes: 2
Reputation: 25445
Here's what I've come up with using Net::HTTPServer. Based on the idea that "It’s OK to Ask and Answer Your Own Questions", I'm posting it here for comment/consideration. What I've done is the following:
First, create a new module at: "./t/TestServer.pm". The contents of this file are:
package TestServer;
use Moose;
use Net::HTTPServer;
use namespace::autoclean;
has 'server' => (
is => "rw",
isa => "Net::HTTPServer",
default => sub {
Net::HTTPServer->new (
port => 8123,
docroot => "t/test-docroot"
)
},
);
sub BUILD {
my $self = shift;
### Spin up the server.
$self->server->Start();
$self->server->Process();
}
### Close up the Moose package.
__PACKAGE__->meta->make_immutable;
1;
Then, update the test "./t/001_get_url.t" file to use it via a fork:
#!/usr/bin/perl
use Modern::Perl;
use Test::More;
use MirrorPage;
### Fork for the server
my $pid = fork();
### Parent process. Holds the tests.
if($pid) {
### Make sure the server has a moment to startup
sleep(2);
my $t = new_ok('MirrorPage', undef, 'Create MirrorPage');
is(
$t->get_url("http://localhost:8123/test-1.json"),
'{ testkey: "testvalue" }',
"Verify the data."
);
}
### Child process. Holds the server.
elsif(defined($pid)) {
use lib "t/";
use TestServer;
my $svr = TestServer->new();
exit; # Should never get here.
}
### Error out if necessary.
else {
die "Can not fork child process.";
}
### Kill the server fork.
kill 1, $pid;
done_testing();
This is working well for me.
Upvotes: 1
Reputation: 492
LWP can fetch files, so you can rewrite $url_to_get
from http://...
to file://...
.
Upvotes: 2
Reputation:
Perhaps:
At the top, fork and do a simple static file server using HTTP::Server::Simple::Static, then at the bottom terminate the child process.
Upvotes: 2
Reputation: 1375
I would mock the HTTP call near the top of your .t file (if you're only wanting to test MirrorPage.pm):
my $mock = new Test::MockObject();
$mock->fake_module( 'LWP::Simple', get => sub { return '{ testkey: "testvalue" }' } );
Upvotes: 3