Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added section on Plack::Test.

  • Loading branch information...
commit 19c40fb775cf674ed90b4dd1c331ada2c630b0af 1 parent 3b56fde
chromatic authored September 19, 2011

Showing 1 changed file with 358 additions and 0 deletions. Show diff stats Hide diff stats

  1. 358  sections/plack_test.pod
358  sections/plack_test.pod
Source Rendered
... ...
@@ -1,3 +1,361 @@
1 1
 =head1 Testing with C<Plack::Test>
2 2
 
3 3
 Z<plack_test>
  4
+
  5
+C<Plack::Test>
  6
+
  7
+The Plack distribution includes a testing module, C<Plack::Test>, which deploys
  8
+your application for you so that you can focus on proving that it works the way
  9
+you intend. Here is the brilliance of Plack both subtle and evident: just as
  10
+you can deploy to mod_perl, Nginx, Starman, Twiggy, or any of myriad live
  11
+servers, you may also deploy to a mocked server which only exists as part of
  12
+your test process.
  13
+
  14
+C<Plack::Test> also lets you deploy to those other C<Plack::Handler> backends
  15
+if you like.
  16
+
  17
+=head2 The C<Plack::Test> Structure
  18
+
  19
+X<C<Test::More>>
  20
+
  21
+C<Plack::Test> exports one function, C<test_psgi>. It takes two parameters, a
  22
+Plack application and a function reference which runs tests against that
  23
+application. In conjunction with the Perl 5 core module C<Test::More>, you
  24
+might write:
  25
+
  26
+=begin programlisting
  27
+
  28
+    use Plack::Test;
  29
+    use Test::More;
  30
+    use My::App;
  31
+
  32
+    my $app = get_my_app();
  33
+    test_psgi $app, sub
  34
+    {
  35
+        my $cb = shift;
  36
+
  37
+        ...
  38
+
  39
+        # tests go here
  40
+    };
  41
+
  42
+=end programlisting
  43
+
  44
+X<C<HTTP::Request>>
  45
+X<C<HTTP::Response>>
  46
+X<C<HTTP::Message>>
  47
+X<C<LWP>>
  48
+
  49
+The testing function has a single parameter, a callback function reference
  50
+which takes an C<HTTP::Request> object and returns an C<HTTP::Response>
  51
+objectN<These modules are part of the C<HTTP::Message> distribution, a
  52
+dependency of C<LWP>.>. This gives you tremendous control over the details of
  53
+making requests and examining responses.
  54
+
  55
+=head2 Basic Tests
  56
+
  57
+Suppose you have a very simple Plack application which compares two values
  58
+provided within the query string of a C<GET> request. If they evaluate to the
  59
+same string values, the application returns a successful response. Otherwise,
  60
+it returns a failure. That application might be:
  61
+
  62
+=begin programlisting
  63
+
  64
+    my $app = sub
  65
+    {
  66
+        my $res  = Plack::Request->new( shift );
  67
+
  68
+        my $want = $res->param( 'want' );
  69
+        my $have = $res->param( 'have' );
  70
+        my $desc = $res->param( 'desc' );
  71
+
  72
+        my ($code, $output) = ( $want eq $have )
  73
+                            ? ( 200, 'ok'      )
  74
+                            : ( 412, 'not ok'  );
  75
+
  76
+        $output .= ' - ' . $desc if $desc;
  77
+
  78
+        return
  79
+        [
  80
+            $code,
  81
+            [ 'Content-Type' => 'text/plain' ],
  82
+            [ $output ]
  83
+         ];
  84
+    };
  85
+
  86
+=end programlisting
  87
+
  88
+=begin sidebar
  89
+
  90
+X<C<Test::Tutorial>>
  91
+X<C<Test::More>>
  92
+
  93
+C<Plack::Test> builds on Perl 5's standard testing tools. If the behavior of
  94
+this example Plack application is unfamiliar, take a few minutes to read
  95
+C<Test::Tutorial>, then see the documentation of C<Test::More>.
  96
+
  97
+=end sidebar
  98
+
  99
+Basic tests for this application must ensure that:
  100
+
  101
+=over 4
  102
+
  103
+=item * providing equivalent values should produce a 200 status code
  104
+
  105
+=item * providing differing values should produce a 412 status code
  106
+
  107
+=item * the HTTP body should reflect the success or failure of the comparison
  108
+
  109
+=item * the optional description should be present in the body when provided
  110
+
  111
+=back
  112
+
  113
+You can easily imagine more tests--what happens without one or both
  114
+parameters?--but these are the minimum possible tests to write.
  115
+
  116
+Assume that C<$app> contains this application. Add the basic C<Plack::Test>
  117
+framework code:
  118
+
  119
+=begin programlisting
  120
+
  121
+    B<#!/usr/bin/env perl>
  122
+
  123
+    B<use Modern::Perl;>
  124
+
  125
+    B<use Test::More;>
  126
+    B<use Plack::Test;>
  127
+    B<use Plack::Request;>
  128
+    B<use HTTP::Request::Common;>
  129
+
  130
+    my $app = sub { ... };
  131
+
  132
+    test_psgi $app, sub { ... };
  133
+
  134
+    B<done_testing();>
  135
+
  136
+=end programlisting
  137
+
  138
+X<C<HTTP::Request::Common>>
  139
+
  140
+The C<use> lines enable a few features found in other Perl 5 modules. In
  141
+particular, C<Test::More> provides several testing functions used later.
  142
+C<HTTP::Request::Common> provides several shortcut functions to create
  143
+C<HTTP::Request> objects.
  144
+
  145
+The C<done_testing()> line informs C<Test::More> that the program has run to
  146
+completion. Some tests may have failed, but the program ran the tests it
  147
+expected to run.
  148
+
  149
+The interesting testing code is within the sub passed to C<test_psgi>:
  150
+
  151
+=begin programlisting
  152
+
  153
+    test_psgi $app, sub
  154
+    {
  155
+        my $cb  = shift;
  156
+
  157
+        my $res = $cb->( GET '/?have=tea;want=tea' );
  158
+        ok $res->is_success, 'Request should succeed when values match';
  159
+        is $res->decoded_content, 'ok',
  160
+            '... with descriptive success message';
  161
+
  162
+        ...
  163
+    };
  164
+
  165
+=end programlisting
  166
+
  167
+When C<test_psgi> gets the application to test and the test code to run, it
  168
+invokes the test code (the anonymous subroutine provided as the second
  169
+argument) and passes that code a callback function reference. Use this
  170
+callback--C<$cb>--to make requests of the application.
  171
+
  172
+The prelude to this code used the module C<HTTP::Request::Common>, which
  173
+provides a C<GET> function. These code snippets are equivalent:
  174
+
  175
+=begin programlisting
  176
+
  177
+    $res = $cb->( GET '/?have=tea;want=tea' );
  178
+    $res = $cb->( HTTP::Request->new( GET => '/?have=tea;want=tea' ) );
  179
+
  180
+=end programlisting
  181
+
  182
+The callback makes the request of the Plack application--using the HTTP scheme
  183
+and C<localhost>, when given URIs without either scheme or host, as in this
  184
+example--and returns an C<HTTP::Response> object. If the application throws an
  185
+uncaught exception, the callback will catch it and return an C<HTTP::Response>
  186
+object with a 500 error and a plain text version of the exception.
  187
+
  188
+Use the response object with the testing functions of C<Test::More> or any
  189
+other Perl testing module. For example, the C<is_success> method returns a
  190
+boolean value signifying whether the response code is successful (C<2I<xx>>,
  191
+and in this case, C<200>). The C<decoded_content> method returns the body of
  192
+the response properly encoded to Perl's internal Unicode formatN<Otherwise you
  193
+run the risk of test failures with Unicode returned from your application.>. As
  194
+the result is a standard Perl string, you can do anything with it that you
  195
+like, such as testing that it is exactly what you expect.
  196
+
  197
+You can go a long way with these two methods of C<HTTP::Response>, such as
  198
+testing that a request did I<not> succeed with a C<200> status code:
  199
+
  200
+=begin programlisting
  201
+
  202
+    $res    = $cb->( GET '/?have=10;want=20' );
  203
+    ok ! $res->is_success, 'Request should fail when values do not match';
  204
+    is   $res->decoded_content, 'not ok', '... with descriptive error';
  205
+
  206
+=end programlisting
  207
+
  208
+... but be careful to construct your query parameters appropriately:
  209
+
  210
+=begin programlisting
  211
+
  212
+    $res    = $cb->( GET '/?have=cow;want=cow;desc=Cow+Comparison' );
  213
+    ok $res->is_success, 'Request should succeed when values do';
  214
+    is $res->decoded_content, 'ok - Cow Comparison',
  215
+        '... including description when provided';
  216
+
  217
+=end programlisting
  218
+
  219
+X<C<URI>>
  220
+
  221
+The C<URI> module can simplify constructing complex queries:
  222
+
  223
+=begin programlisting
  224
+
  225
+    my $uri = URI->new( '/' );
  226
+    $uri->query_form( have => 'cow', want => 'cow', desc => 'Cow Comparison' );
  227
+    $res    = $cb->( GET $uri );
  228
+
  229
+=end programlisting
  230
+
  231
+Although these two methods are by far the most useful, see the documentation of
  232
+C<HTTP::Request>, C<HTTP::Response>, and C<HTTP::Message> for many more methods
  233
+to create and test complex requests and responses. For example, to test the
  234
+content type and charset of a response:
  235
+
  236
+=begin programlisting
  237
+
  238
+    is $res->content_type,    'text/plain', '... with plain text content';
  239
+    is $res->content_charset, 'US-ASCII',   '... in ASCII';
  240
+
  241
+=end programlisting
  242
+
  243
+... though of course your applications get more interesting when they handle
  244
+more than mere ASCII.
  245
+
  246
+=head2 Running Tests
  247
+
  248
+As with the standard Perl testing approach, these automated tests are merely
  249
+valid Perl programs which produce TAPN<Test Anything Protocol; see
  250
+U<http://testanything.org/>.> output. You can run them directly:
  251
+
  252
+=begin screen
  253
+
  254
+    $ perl t/ok_over_http.t
  255
+    ok 1 - Request should succeed when values do
  256
+    ok 2 - ... with descriptive success message
  257
+    ok 3 - Request should fail when values do not match
  258
+    ok 4 - ... with descriptive error
  259
+    ok 5 - Request should succeed when values do
  260
+    ok 6 - ... including description when provided
  261
+    ok 7 - ... with plain text content
  262
+    ok 8 - ... in ASCII
  263
+    1..8
  264
+
  265
+=end screen
  266
+
  267
+X<C<prove>>
  268
+
  269
+... or with the C<prove> utility provided as part of Perl's testing tools:
  270
+
  271
+=begin screen
  272
+
  273
+    $ perl t/ok_over_http.t
  274
+    t/ok_over_http.t .. ok
  275
+    All tests successful.
  276
+    Files=1, Tests=6...
  277
+    Result: PASS
  278
+
  279
+=end screen
  280
+
  281
+Note that more complex projects might require additional setup of the Perl
  282
+library search path, for example, using C<perl -Ilib t/test_file.t> or C<prove
  283
+-l t/test_file.t>. If you use a framework such as Catalyst, Mojolicious, or
  284
+Dancer, the standard generated scaffolding should generate example tests as
  285
+well as a test runner.
  286
+
  287
+=head2 Managing Tests
  288
+
  289
+C<Plack::Test> offers lots of power and flexibility for testing small details
  290
+of your applications, but it offers no abstraction beyond the C<test_psgi>
  291
+function. Any structure to your test files comes from you or other testing
  292
+modules. One possible test pattern uses named test functions to separate
  293
+logical components of the application:
  294
+
  295
+=begin programlisting
  296
+
  297
+    #!/usr/bin/env perl
  298
+
  299
+    use Modern::Perl;
  300
+
  301
+    use Test::More;
  302
+    use Plack::Test;
  303
+    use Package::Stash;
  304
+    use namespace::autoclean;
  305
+    use HTTP::Request::Common;
  306
+
  307
+    exit main();
  308
+
  309
+    sub main
  310
+    {
  311
+        my $app   = get_app();
  312
+        my $stash = Package::Stash->new( __PACKAGE__ );
  313
+
  314
+        test_psgi $app, __PACKAGE__->can( $_ )
  315
+            for grep /^test_/, $stash->list_all_symbols( 'CODE' );
  316
+
  317
+        done_testing();
  318
+
  319
+        return 0;
  320
+    }
  321
+
  322
+=end programlisting
  323
+
  324
+X<C<Package::Stash>>
  325
+X<C<UNIVERSAL>>
  326
+
  327
+This approach wraps all test dispatch in a C<main> function to encapsulate it
  328
+from any other code outside of functions. The only interesting part of this
  329
+code is the introspection code, which uses C<Package::Stash> and C<can> (from
  330
+Perl's core C<UNIVERSAL> package) to find functions I<defined> in this package
  331
+named C<test_I<name>>.
  332
+
  333
+=begin sidebar
  334
+
  335
+X<C<namespace::autoclean>>
  336
+
  337
+Why doesn't this code find C<test_psgi> and cause strange errors? The
  338
+C<namespace::autoclean> module removes that imported function I<before> C<main>
  339
+runs. Even though C<main> still knows how to call it, it's no longer visible to
  340
+C<Package::Stash> when the introspection code runs.
  341
+
  342
+=end sidebar
  343
+
  344
+All of this means that it's easy to add new tests to the file:
  345
+
  346
+=begin programlisting
  347
+
  348
+    sub test_root
  349
+    {
  350
+        my $cb  = shift;
  351
+        my $res = $cb->GET( '/' );
  352
+
  353
+        ok $res->is_redirect, '/ request should redirect';
  354
+        like $res->header( 'Location' ), qr{/index$}, '... to /index';
  355
+
  356
+        ...
  357
+    }
  358
+
  359
+=end programlisting
  360
+
  361
+... as they're merely named functions which run as C<Plack::Test> callbacks.

0 notes on commit 19c40fb

Please sign in to comment.
Something went wrong with that request. Please try again.