recipe for happy forking in a Test::More test script

markjugg on 2005-12-17T22:21:42

I recently picked up work again on CGI::Uploader

As part of updating the test suite, I doing away with the homemade hack I had for simulating a file upload. The best solution for this now appears to be to use HTTP::Request::AsCGI. However, it apparently depends on a newer 'libwww' than I have, so I devised a solution that uses HTTP::Request::Common directly.

This involved forking in the test script, with the parent serving the content and the child process working like a web client.

In the process I ran into and overcame some gotchas with forking with Test::More that seemed worth having better documentation for.

In summary, it is possible with these caveats:

  • You have to declare a plan eventually, our you'll get a complaint at the end about "Don't know which tests failed".
  • You need this: Test::More->builder->no_ending(1);
  • After you fork, you can't run anymore tests in the parent.
Here's the basic recipe I used for the forking par, with much help and code borrowing from Cees Hek, who in turn got key tips from adrianh:
# Fake a web server CGI request by building a proper
# POST request, setting some ENV variables and
# forking a child process that gets the POST
# content from STDIN
my $req = &HTTP::Request::Common::POST(
    '/dummy_location',
    Content_Type => 'form-data',
    Content      => [
        name   => 'name1',
        test   => 'name2',
        image1 => ["t/image.jpg"],
    ]
);
$ENV{REQUEST_METHOD} = 'POST';
$ENV{CONTENT_TYPE}   = 'multipart/form-data';
$ENV{CONTENT_LENGTH} = $req->content_length;
if ( open( CHILD, "|-" ) ) {    # cparent
    print CHILD $req->content;
    close CHILD;
    exit 0;
}

# at this point, we're in a new (child) process
# and CGI.pm can read the POST params from STDIN
# as in a real request
my $q = CGI->new;