Why is Test::WWW::Mechanize::PSGI using a port?
Asked Answered
C

2

9

I have some code that looks like this:

use SomeApp;
use Test::WWW::Mechanize::PSGI;                                                                                                                                         
my $mech = Test::WWW::Mechanize::PSGI->new(
    app  => sub { SomeApp->run(@_) },
);
$mech->get_ok('/');

However, as soon as get_ok() is called, I get the following warning:

PSGI error: failed to listen to port 8080: Address already in use at .../5.18.1/HTTP/Server/PSGI.pm line 94.
HTTP::Server::PSGI::setup_listener('HTTP::Server::PSGI=HASH(0x7fe6622fad60)') called at .../5.18.1/HTTP/Server/PSGI.pm line 54

And yes, I'm using that port for something else. From the docs of Test::WWW::Mechanize::PSGI:

This module allows you to test PSGI web applications but does not require a server or issue HTTP requests. Instead, it passes the HTTP request object directly to PSGI.

So in theory, I shouldn't need to specify a port, but I get the above warning and pages fetched return a 500 (they work fine in the browser). What am I missing?

  • Test::WWW::Mechanize::PSGI version 0.35
  • Plack version 1.0030
  • Catalyst version 5.90051

Changing MyApp->run to MyApp->psgi_app results in:

Can't call method "request" on an undefined value at .../5.18.1/Test/WWW/Mechanize/PSGI.pm line 47.

This error can be replicated with:

catalyst.pl MyApp
cd MyApp
# run the test program above
Cerracchio answered 28/12, 2013 at 9:33 Comment(2)
I'm not sure what SomeApp contains, but have you ever tried Plack::Util::load_psgi? my $mech = Test::WWW::Mechanize::PSGI->new(app => Plack::Util::load_psgi('SomeApp'));Ens
emix: that generates Error while loading MyApp.pm: Setting config after setup has been run is not allowed even with a plain catalyst.pl SomeApp. I've posted a workaround as an answer. Also, if I wrap it in a sub (required) and remove the "use SomeApp", I get Can't call method "request" on an undefined value.Cerracchio
O
15

Catalyst's run method would actually run the HTTP server (via Plack/PSGI!) for a development, which is not what you want with testing via PSGI (without running a server). You need: app => MyApp->psgi_app, without an extra sub block, since psgi_app supposedly returns the PSGI app itself.

The error message "Can't call method 'request' on ..." is a common error when your app returns something that is not correct per PSGI spec. The message has been improved a bit on the git master, but it's essentially a user error since you're basically returning sub { $app } when it expects just $app.

More documentation on the PSGI support with Catalyst is available with perldoc Catalyst::PSGI.

Ostium answered 28/12, 2013 at 10:36 Comment(1)
Thank you. Using MyApp->psgi_app without the sub solved it. This is a legacy application I've been trying to upgrade, as you speculated on Twitter, and there are many tiny changes like this I'm finding.Cerracchio
C
4

Matt Trout mentioned LWP::Protocol::PSGI as a workaround. It hijacks HTTP to make this work:

use Test::WWW::Mechanize;
use LWP::Protocol::PSGI;
use MyApp;

LWP::Protocol::PSGI->register( MyApp->psgi_app(@_) );
my $mech = Test::WWW::Mechanize->new;

# first GET must be absolute
$mech->get('http://localhost/login');
say $mech->content;

# then we can switch to relative
$mech->get('/login');
say $mech->content;

In short, the above is more or less cargo-culted (as I don't understand why the first version failed), but it's enough for me to move forward.

Cerracchio answered 28/12, 2013 at 10:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.