Use Test::WWW::Mechanize::PSGI for tests
Hi team. I wanted to wait for the developper meeting to mention this, but sniping issue #2000 felt way too tempting
I would like to suggest adopting a new framework for doing our tests.
The current way of doing things feels very verbose to me, we need to craft every HTTP request, take care of handling cookies, follow redirections... We do have a test-lib.pl but it doesn't hide that much complexity. And because we need to manually handle cookies, our current tests cannot handle the case where a change suddenly sends unexpected pdata... because we never wrote the corresponding handlig in the original test.
WWW::Mechanize is a nice library that can take care of all this. And after playing around a little with it, I'm convinced that it could help us a lot.
Demo time
Consider the following, complete unit test :
use Test::More;
use strict;
require 't/test-lib.pm';
# The lib that makes the magic happen
use Test::WWW::Mechanize::PSGI;
# Instanciate a new test client. The package is named LLNG::Manager::Test for
# some reason but it does instanciate a portal PSGI app.
my $mech = Test::WWW::Mechanize::PSGI->new(
app => LLNG::Manager::Test->new(
{ ini => { logLevel => 'error', useSafeJail => 1, requireToken =>1 } } )->app
);
# Doing a GET request and ensuring a code 200 is one simple line
$mech->get_ok( 'http://auth.example.com/', 'Get homepage' );
# Now this is where Mechanize truely shines
# The submit_form_ok function will scan the previously obtained HTML
# and locate a form that has a user and password field, then will POST it
# along with any hidden fields (token), to its target
# No need to specify the target URL, no need to specify the hidden fields
# Mechanize will automatically follow the redirection and add any received
# cookies
$mech->submit_form_ok( {
with_fields => {
user => 'dwho',
password => 'dwho',
}
},
"Post Login form"
);
# And now, all we need to check is that after doing the POST we obtained
# the correct result page (app menu)
$mech->content_like( qr/connectedAs.*dwho/, "Logged in" );
# Now for a logout flow
$mech->get_ok( 'http://auth.example.com/logout', 'Log out' );
$mech->content_like( qr/<span trmsg="47">/, "Logout confirmation message" );
# Make sure the result page contains a go to portal button and follow it
$mech->follow_link_ok({ text => 'Go to portal' });
# Make sure we are now logged off
ok( $mech->form_with_fields ( ('user', 'password') ), 'Found login form');
# Cleanup, as usual
clean_sessions();
done_testing();
Of course, this is just a simple test, but any complex web flow that consists in multiple redirections and lots of cookies being passed around (SP-to-proxy-to-IDP tests) would be just as easy to write, using Plack::Builder to map different URLs to each app. Overriding LWP to allow app-to-app web requests also works fine.
Downsides
Of course, everything isn't rainbows and unicorns. So here are a few caveats:
- The elephant in the room is that we certainly will not rewrite every test overnight. But we can start writing new ones with it.
- We need to add Test::WWW::Mechanize::PSGI as a dependancy. It's packaged in Debian but not in Centos. thankfully, it consists in WWW::Mechanize (already in Centos) + 2 small .pm files that we could easily copy into our source tree.
- Assertions on cookies are not very easy to do, because Mechanize hides complexity a little too well sometimes. So we should probably only use it for high-level testing of complex web flows, and keep the low level tests (such as ensuring LLNG reacts properly when it's being sent forged headers, faked access tokens, etc) in the previous format.
We can discuss this in more details in our November meeting, unless you feel like this is a silly idea