Alan W. Smith
Alan W. Smith

Reputation: 25445

Is there a simple way to setup a local, self-contained web server for perl testing?

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

Answers (6)

Joe McMahon
Joe McMahon

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

Alan W. Smith
Alan W. Smith

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

ruz
ruz

Reputation: 492

LWP can fetch files, so you can rewrite $url_to_get from http://... to file://....

Upvotes: 2

user1167974
user1167974

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

Chris J
Chris J

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

Hikalea
Hikalea

Reputation: 119

Although it's OS specific, I'm sure Apache is your answer.

Upvotes: -1

Related Questions