but also provide automated regression testing as you upgrade various
pieces of your application over time.
-You can check out the source code for this example from the Catalyst
-Subversion repository as per the instructions in
+Source code for the tutorial in included in the F</home/catalyst/Final>
+directory of the Tutorial Virtual machine (one subdirectory per
+chapter). There are also instructions for downloading the code in
L<Catalyst::Manual::Tutorial::01_Intro>.
For an excellent introduction to learning the many benefits of testing
ok( request('/books')->is_redirect, 'Request should succeed' );
-4) Add the following statement to the top of C<t/view_TT.t>:
+4) Add the following statement to the top of C<t/view_HTML.t>:
use MyApp;
-As you can see in the C<prove> command line above, the C<--lib> option
-is used to set the location of the Catalyst C<lib> directory. With this
-command, you will get all of the usual development server debug output,
-something most people prefer to disable while running tests cases.
-Although you can edit the C<lib/MyApp.pm> to comment out the C<-Debug>
-plugin, it's generally easier to simply set the C<CATALYST_DEBUG=0>
-environment variable. For example:
+As you can see in the C<prove> command line above, the C<-l> option (or
+C<--lib> if you prefer) is used to set the location of the Catalyst
+C<lib> directory. With this command, you will get all of the usual
+development server debug output, something most people prefer to disable
+while running tests cases. Although you can edit the C<lib/MyApp.pm> to
+comment out the C<-Debug> plugin, it's generally easier to simply set
+the C<CATALYST_DEBUG=0> environment variable. For example:
$ CATALYST_DEBUG=0 prove -wl t
Another useful option is the C<verbose> (C<-v>) option to C<prove>. It
prints the name of each test case as it is being run:
- $ CATALYST_DEBUG=0 TEST_POD=1 prove -vwl t
+ $ CATALYST_DEBUG=0 prove -vwl t
=head1 RUNNING A SINGLE TEST
Although the Catalyst helper scripts provide a basic level of checks
"for free," testing can become significantly more helpful when you write
-your own script to exercise the various parts of your application. The
+your own tests to exercise the various parts of your application. The
L<Test::WWW::Mechanize::Catalyst> module is very popular for writing
these sorts of test cases. This module extends L<Test::WWW::Mechanize>
(and therefore L<WWW::Mechanize>) to allow you to automate the action of
To create a sample test case, open the C<t/live_app01.t> file in your
editor and enter the following:
- #!/usr/bin/perl
-
+ #!/usr/bin/env perl
+
use strict;
use warnings;
use Test::More;
-
+
# Need to specify the name of your app as arg on next line
# Can also do:
# use Test::WWW::Mechanize::Catalyst "MyApp";
-
+
BEGIN { use_ok("Test::WWW::Mechanize::Catalyst" => "MyApp") }
-
+
# Create two 'user agents' to simulate two different users ('test01' & 'test02')
my $ua1 = Test::WWW::Mechanize::Catalyst->new;
my $ua2 = Test::WWW::Mechanize::Catalyst->new;
-
+
# Use a simplified for loop to do tests that are common to both users
# Use get_ok() to make sure we can hit the base URL
# Second arg = optional description of test (will be displayed for failed tests)
# Use content_contains() to match on text in the html body
$_->content_contains("You need to log in to use this application",
"Check we are NOT logged in") for $ua1, $ua2;
-
+
# Log in as each user
# Specify username and password on the URL
$ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
username => 'test02',
password => 'mypass',
});
-
+
# Go back to the login page and it should show that we are already logged in
$_->get_ok("http://localhost/login", "Return to '/login'") for $ua1, $ua2;
$_->title_is("Login", "Check for login page") for $ua1, $ua2;
$_->content_contains("Please Note: You are already logged in as ",
"Check we ARE logged in" ) for $ua1, $ua2;
-
+
# 'Click' the 'Logout' link (see also 'text_regex' and 'url_regex' options)
$_->follow_link_ok({n => 4}, "Logout via first link on page") for $ua1, $ua2;
$_->title_is("Login", "Check for login title") for $ua1, $ua2;
$_->content_contains("You need to log in to use this application",
"Check we are NOT logged in") for $ua1, $ua2;
-
+
# Log back in
- $ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
- $ua2->get_ok("http://localhost/login?username=test02&password=mypass", "Login 'test02'");
+ $ua1->get_ok("http://localhost/login?username=test01&password=mypass",
+ "Login 'test01'");
+ $ua2->get_ok("http://localhost/login?username=test02&password=mypass",
+ "Login 'test02'");
# Should be at the Book List page... do some checks to confirm
$_->title_is("Book List", "Check for book list title") for $ua1, $ua2;
-
+
$ua1->get_ok("http://localhost/books/list", "'test01' book list");
$ua1->get_ok("http://localhost/login", "Login Page");
$ua1->get_ok("http://localhost/books/list", "'test01' book list");
-
+
$_->content_contains("Book List", "Check for book list title") for $ua1, $ua2;
# Make sure the appropriate logout buttons are displayed
$_->content_contains("/logout\">User Logout</a>",
"'test01' should have a create link");
$ua2->content_lacks("/books/form_create\">Admin Create</a>",
"'test02' should NOT have a create link");
-
+
$ua1->get_ok("http://localhost/books/list", "View book list as 'test01'");
-
+
# User 'test01' should be able to create a book with the "formless create" URL
$ua1->get_ok("http://localhost/books/url_create/TestTitle/2/4",
"'test01' formless create");
$ua1->content_contains("by 'Stevens'", "Check author added OK");
$ua1->content_contains("with a rating of 2.", "Check rating added");
# Try a regular expression to combine the previous 3 checks & account for whitespace
- $ua1->content_like(qr/Added book 'TestTitle'\s+by 'Stevens'\s+with a rating of 2./, "Regex check");
-
+ $ua1->content_like(qr/Added book 'TestTitle'\s+by 'Stevens'\s+with a rating of 2./,
+ "Regex check");
+
# Make sure the new book shows in the list
$ua1->get_ok("http://localhost/books/list", "'test01' book list");
$ua1->title_is("Book List", "Check logged in and at book list");
$ua1->content_contains("Book List", "Book List page test");
$ua1->content_contains("TestTitle", "Look for 'TestTitle'");
-
+
# Make sure the new book can be deleted
# Get all the Delete links on the list page
my @delLinks = $ua1->find_all_links(text => 'Delete');
$ua1->get_ok($delLinks[$#delLinks]->url, 'Delete last book');
# Check that delete worked
$ua1->content_contains("Book List", "Book List page test");
- $ua1->content_contains("Book deleted", "Book was deleted");
-
+ $ua1->content_like(qr/Deleted book \d+/, "Deleted book #");
+
# User 'test02' should not be able to add a book
$ua2->get_ok("http://localhost/books/url_create/TestTitle2/2/5", "'test02' add");
$ua2->content_contains("Unauthorized!", "Check 'test02' cannot add");
-
+
done_testing;
The C<live_app.t> test cases uses copious comments to explain each step
of the process. In addition to the techniques shown here, there are a
variety of other methods available in L<Test::WWW::Mechanize::Catalyst>
-(for example, regex-based matching). Consult the documentation for more
-detail.
+(for example, regex-based matching). Consult
+L<Test::WWW::Mechanize::Catalyst>, L<Test::WWW::Mechanize>,
+L<WWW::Mechanize>, and L<Test::More> for more detail.
B<TIP>: For I<unit tests> vs. the "full application tests" approach used
by L<Test::WWW::Mechanize::Catalyst>, see L<Catalyst::Test>.
Another approach to see the full HTML content at the failure point in a
series of tests would be to insert a "C<$DB::single=1;> right above the
-location of the failure and run the test under the perl debugger (with
+location of the failure and run the test under the Perl debugger (with
C<-d>) as shown above. Then you can use the debugger to explore the
state of the application right before or after the failure.
my $dsn = $ENV{MYAPP_DSN} ||= 'dbi:SQLite:myapp.db';
__PACKAGE__->config(
schema_class => 'MyApp::Schema',
-
+
connect_info => {
dsn => $dsn,
user => '',
=head2 DATABASE CONFIG SWITCHING USING MULTIPLE CONFIG FILES
-By utilizing L<Catalyst::Plugin::ConfigLoader>s functionality for
-loading multiple config files based on environment variables you can
-override your default (production) database connection settings.
+L<Catalyst::Plugin::ConfigLoader> has functionality to load
+multiple config files based on environment variables, allowing you to
+override your default (production) database connection settings during
+development (or vice versa).
Setting C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> to 'testing' in your test
script results in loading of an additional config file named
use strict;
use warnings;
use Test::More;
-
+
BEGIN {
$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX } = 'testing';
}
-
+
eval "use Test::WWW::Mechanize::Catalyst 'MyApp'";
plan $@
? ( skip_all => 'Test::WWW::Mechanize::Catalyst required' )
: ( tests => 2 );
-
+
ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
-
+
$mech->get_ok( 'http://localhost/foo' );
+You can jump to the next chapter of the tutorial here:
+L<Advanced CRUD|Catalyst::Manual::Tutorial::09_AdvancedCRUD>
+
+
=head1 AUTHOR
Kennedy Clark, C<hkclark@gmail.com>
Feel free to contact the author for any errors or suggestions, but the
best way to report issues is via the CPAN RT Bug system at
-<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
-
-The most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
Creative Commons Attribution Share-Alike License Version 3.0
(L<http://creativecommons.org/licenses/by-sa/3.0/us/>).