Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added Test::WWW::Mechanize::PSGI section.

  • Loading branch information...
commit f39945e9801c9e2b2aae817d342fa8a9f99f2d4d 1 parent 19c40fb
@chromatic authored
View
53 examples/okay_app/okay_mech.t
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+
+use Modern::Perl;
+
+use utf8;
+use open ':encoding(utf8)';
+
+use Test::More;
+use Test::WWW::Mechanize::PSGI;
+use Plack::Request;
+
+my $app = sub
+{
+ my $res = Plack::Request->new( shift );
+
+ my $want = $res->param( 'want' );
+ my $have = $res->param( 'have' );
+ my $desc = $res->param( 'desc' );
+
+ my ($code, $output) = ( $want eq $have )
+ ? ( 200, 'ok' )
+ : ( 412, 'not ok' );
+
+ $output .= ' - ' . $desc if $desc;
+ return [ $code, [ 'Content-Type' => 'text/plain' ], [ $output ] ];
+};
+
+my $mech = Test::WWW::Mechanize::PSGI->new( app => $app );
+$mech->get_ok( '/?have=foo;want=foo',
+ 'Request should succeed when values match' );
+$mech->content_is( 'ok', '... with descriptive success message' );
+
+$mech->get( '/?have=10;want=20' );
+ok ! $mech->success, 'Request should fail when values do not match';
+
+$mech->content_is( 'not ok', '... with descriptive error' );
+
+my $uri = URI->new( '/' );
+$uri->query_form( have => 'cow', want => 'cow', desc => 'Cow Comparison' );
+$mech->get_ok( $uri, 'Request should succeed when values do' );
+$mech->content_is( 'ok - Cow Comparison',
+ '... including description when provided' );
+is $mech->content_type, 'text/plain', '... with plain text content';
+is $mech->response->content_charset, 'US-ASCII', '... in ASCII';
+
+$mech->post( '/', [ have => 'cow', want => 'pig', desc => 'æ' ] );
+ok ! $mech->success, 'Request should fail given different values';
+$mech->content_is( "not ok - \x{00E6}",
+ '... including description when provided' );
+is $mech->content_type, 'text/plain', '... with plain text content';
+is $mech->response->content_charset, 'UTF-8', '... encoded as UTF-8';
+
+done_testing();
157 sections/mech_psgi.pod
@@ -1,3 +1,160 @@
=head1 Testing with C<Test::WWW::Mechanize::PSGI>
Z<mech_psgi>
+
+X<C<Test::WWW::Mechanize::PSGI>>
+
+C<Plack::Test> offers fine-grained control over the details of HTTP requests
+and responses. It's most appropriate for testing handlers, middleware, and
+other Plack components which modify requests and responses. For testing
+applications, C<Test::WWW::Mechanize::PSGI> may be more useful.
+
+Like C<Plack::Test>, C<Test::WWW::Mechanize::PSGI> can run your PSGI
+application it the same process without needing to communicate with a running
+HTTP server. Unlike C<Plack::Test>, TWMP cannot currently communicate with a
+running HTTP server. For most application testing, this is unlikely to be a
+problem.
+
+=head2 The C<Test::WWW::Mechanize::PSGI> Structure
+
+X<C<Test::WWW::Mechanize>>
+X<C<WWW::Mechanize>>
+X<C<LWP::UserAgent>>
+
+C<Test::WWW::Mechanize::PSGI> is an object oriented module and subclasses
+C<Test::WWW::Mechanize> which in turn subclasses C<WWW::Mechanize>, itself a
+child of C<LWP::UserAgent>, to provide several test assertions as methods.
+Start your testing by creating a new object. Pass your PSGI application to the
+constructor. The resulting object can perform HTTP requests and you can examine
+it or call test assertions on it.
+
+=begin programlisting
+
+ use Test::More;
+ use Test::WWW::Mechanize::PSGI;
+
+ use My::App;
+
+ my $app = get_my_app();
+ my $mech = Test::WWW::Mechanize::PSGI->new( app => $app );
+
+ # ready for testing
+
+=end programlisting
+
+=head2 Basic Tests
+
+Given the example testing app (L<basic_psgi_test_app>) from the C<Plack::Test>
+section, the same testing strategy applies. Basic tests must verify that HTTP
+C<GET> and C<POST> methods with the appropriate query or form parameters
+produce the expected results. The Mechanize approach simplifies the
+construction and examination of the requests.
+
+The C<get_ok()> method performs a C<GET> request and asserts that the request
+succeeded by returning an HTTP C<2xx> status code. Performing a request stores
+within the object the I<contents> of the response, so that subsequent methods
+can examine the Mechanize object and make further test assertions. This is
+easier to show than to explain:
+
+=begin programlisting
+
+ $mech->get_ok( '/?have=foo;want=foo',
+ 'Request should succeed when values match' );
+ $mech->content_is( 'ok', '... with descriptive success message' );
+
+=end programlisting
+
+If the C<GET> fails, the first test assertion will fail as well. There is no
+corresponding C<get_not_ok> method, so you must use the C<success> method
+provided by the grandparent class C<WWW::Mechanize> instead:
+
+=begin programlisting
+
+ $mech->get( '/?have=10;want=20' );
+ ok ! $mech->success, 'Request should fail when values do not match';
+
+ $mech->content_is( 'not ok', '... with descriptive error' );
+
+=end programlisting
+
+X<C<URI>>
+
+You can construct your own URIs with query strings with the C<URI> module:
+
+=begin programlisting
+
+ my $uri = URI->new( '/' );
+ $uri->query_form( have => 'cow',
+ want => 'cow',
+ desc => 'Cow Comparison' );
+
+ $mech->get_ok( $uri, 'Request should succeed when values do' );
+ $mech->content_is( 'ok - Cow Comparison',
+ '... including description when provided' );
+
+=end programlisting
+
+... and you may poke into the C<HTTP::Response> object contained within the
+Mech object when Mech doesn't provide test assertions for specific information:
+
+=begin programlisting
+
+ is $mech->content_type, 'text/plain', '... with plain text content';
+ is $mech->response->content_charset, 'US-ASCII', '... in ASCII';
+
+=end programlisting
+
+Performing C<POST> requests is similar. The first argument to the method is the
+URI to which to post. The second is an array reference of key/value pairs to
+use as C<POST>ed form data:
+
+=begin programlisting
+
+ $mech->post( '/', [ have => 'cow', want => 'pig', desc => 'æ' ] );
+ ok ! $mech->success, 'Request should fail given different values';
+ $mech->content_is( "not ok - \x{00E6}",
+ '... including description when provided' );
+ is $mech->content_type, 'text/plain', '... with plain text content';
+ is $mech->response->content_charset, 'UTF-8', '... encoded as UTF-8';
+
+=end programlisting
+
+Other useful action methods are C<head_ok()> and C<put_ok()>, which perform
+HTTP C<HEAD> and C<PUT> requests respectively.
+
+=head2 Stateful Mechanize
+
+As you've noticed, the Mech object remembers the state of the response. Not
+only does this allow you to make test assertions against the response such as
+C<content_is()> and C<content_like()>, but you can submit forms, click buttons,
+and follow links on the content. See the documentation for
+C<Test::WWW::Mechanize> for more details.
+
+This encapsulation and statefulness affords you the interesting possibility of
+testing application behavior with and without certain middleware active or with
+and without user authentication active, for example.
+
+=head2 When to use Mechanize
+
+C<WWW::Mechanize> has a larger ecosystem than C<Plack::Test> does, due to its
+age. Several CPAN distributions provide useful extensions.
+
+=over 4
+
+=item * C<Test::WWW::Mechanize::Driver> supports data-driven testing by reading
+YAML files containing details of requests to make and tests to run. You can
+provide a C<Test::WWW::Mechanize::PSGI> object to its constructor.
+
+=item * C<WWW::Mechanize::Plugin::Ajax> adds Ajax support to C<WWW::Mechanize>.
+
+=item * C<Test::WWW::Mechanize::Mojo> is a Mechanize built to test applications
+written using the Mojolicious web framework.
+
+=item * C<Test::WWW::Mechanize::Catalyst> does the same for the Catalyst
+framework.
+
+=back
+
+Unfortunately, C<WWW::Mechanize>'s subclassing design can make it difficult for
+some of these modules to interact with others. In time perhaps a newer design
+will allow more flexibility and composition possibilities.
View
2  sections/plack_test.pod
@@ -54,6 +54,8 @@ making requests and examining responses.
=head2 Basic Tests
+Z<basic_psgi_test_app>
+
Suppose you have a very simple Plack application which compares two values
provided within the query string of a C<GET> request. If they evaluate to the
same string values, the application returns a successful response. Otherwise,
Please sign in to comment.
Something went wrong with that request. Please try again.