Dancer + Jasmine
So I’m currently having fun learning Angular. Well, that’s not exactly accurate. A few days ago I had fun learning Angular; now I’m learning the whole surrounding JavaScript ecosystem. And oh boy are we talking Amazonian-grade type of ecosystem — turn a rock, any rock, and you are guaranteed to have 5 new frameworks scamper away.
Anyway, that’s not the point. The point is, it’s a new programming vista, but some things don’t change. Like the need to have tests. Although the backend of my current project is Dancer-based (natch), Angular makes it a mostly front-end application. Just testing the Perl code won’t do (although, y’know, it’s a good start). So, what to use to test the JavaScript core of the application? Looking around, Jasmine seemed to be a decent choice, so I decided to give it a whirl.
Turns out it’s not too bad. And there are quite a few ways to run the tests. But… How about really integrating those tests?
So I give you Dancer::Plugin::Test::Jasmine, which auto-inject tests in your web pages. Lemme show you how.
First, you add the plugin configuration to your app config.yml
:
plugins: ‘Test::Jasmine’: specs_dir: t/specs
lib_dir: /path/to/Dancer-Plugin-Test-Jasmine/share
Nothing outlandish, just the directory where the jasmine specs files are. Plus optionally the directory where the jasmine libs are, although it can be omitted and provided directly by the plugin.
Then you use
the plugin in the app. It’ll give you two new keywords,
jasmine_includes
and jasmine_tests
which you must feed to your templates
(I suspect I’ll make that automagically dealt with in a subsequent version).
package MyApp;
use Dancer ':syntax';
use Dancer::Plugin::Test::Jasmine;
get '/' => sub {
template 'index', {
jasmine_includes => jasmine_includes(),
jasmine_tests => jasmine_tests(),
};
};
...;
Then in the templates you put jasmine_includes
in the headers (it’ll add
all the <script>
s and <link>
s for jasmine), and jasmine_tests
at the end of the <body>
(it’ll inject the tests themselves).
And, basically, you’re done. You can access the application normally:
But if you add ?test=sometest
to the url, that test will be loaded along the
page and executed. For example, provided that you have the file
t/specs/hello.js
being
describe("A testsuite", function() {
it( "should report a success", function(){
expect( $('h1:first').text() ).toBe('Perl is dancing');
});
it( "will fail", function(){
expect( 0 ).toBeTruthy();
});
});
you’ll get
So all jasmine tests are now only a parameter away. Ain’t that nifty?
But wait! There’s more!
This is quite nice for manual inspection and development assist. But for full-on testing, I want to be able to automate the whole shebang.
Having Jasmine output its results in a easy-harvestable format is not hard — there’s a reporter that gives us a perfect good JSON representation to play with. The tricky part is deciding how to run the application. We can’t rely on good ol’ Test::WWW::Mechanize::PSGI alone as the tests need to go through a JavaScript engine. There is always Test::WWW::Selenium, but we’ll go one step further and use WWW::Mechanize::PhantomJS, so that we don’t have to deal with a Selenium server nor a browser. How do we do it? Like that:
use strict;
use warnings;
use Test::More;
use JSON qw/ from_json /;
use Test::TCP;
use WWW::Mechanize::PhantomJS;
use Dancer::Plugin::Test::Jasmine::Results;
Test::TCP::test_tcp(
client => sub {
my $port = shift;
my $mech = WWW::Mechanize::PhantomJS->new;
$mech->get("http://localhost:$port?test=hello");
jasmine_results from_json
$mech->eval_in_page('jasmine.getJSReportAsString()';
},
server => sub {
my $port = shift;
use Dancer;
use MyApp;
Dancer::Config->load;
set( startup_info => 0, port => $port );
Dancer->dance;
},
);
done_testing;
And here what it spits out:
# Subtest: jasmine test
# duration: 0.005s
not ok 1
# Subtest: A testsuite
# duration: 0.005s
not ok 1
# Subtest: should report a success
# duration: 0.003s
ok 1
1..1
ok 2 - should report a success
# Subtest: will fail
# duration: 0.001s
not ok 1
1..1
# Looks like you failed 1 test of 1.
not ok 3 - will fail
1..3
# Looks like you failed 2 tests of 3.
not ok 2 - A testsuite
1..2
# Looks like you failed 2 tests of 2.
not ok 1 - jasmine test
1..1
# Looks like you failed 1 test of 1.
TA-DAAAH!