X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FTutorial%2FTesting.pod;fp=lib%2FCatalyst%2FManual%2FTutorial%2FTutorial%2FTesting.pod;h=f3b2b6412e68a293ba60971f1dea7e2f5762d3ac;hp=0000000000000000000000000000000000000000;hb=d442cc9fbdf45a28fe02f13552f44e3e2f7ec22e;hpb=4be096acef2b81af64442da618294f69630029a3 diff --git a/lib/Catalyst/Manual/Tutorial/Tutorial/Testing.pod b/lib/Catalyst/Manual/Tutorial/Tutorial/Testing.pod new file mode 100644 index 0000000..f3b2b64 --- /dev/null +++ b/lib/Catalyst/Manual/Tutorial/Tutorial/Testing.pod @@ -0,0 +1,351 @@ +=head1 NAME + +Catalyst::Manual::Tutorial::Testing - Catalyst Tutorial - Part 7: Testing + + +=head1 OVERVIEW + +This is B for the Catalyst tutorial. + +L + +=over 4 + +=item 1 + +L + +=item 2 + +L + +=item 3 + +L + +=item 4 + +L + +=item 5 + +L + +=item 6 + +L + +=item 7 + +B + +=item 8 + +L + +=item 9 + +L + +=back + +=head1 DESCRIPTION + +You may have noticed that the Catalyst Helper scripts automatically +create basic C<.t> test scripts under the C directory. This part of +the tutorial briefly looks at how these tests can be used to not only +ensure that your application is working correctly at the present time, +but also provide automated regression testing as you upgrade various +pieces of your application over time. + +You can checkout the source code for this example from the catalyst +subversion repository as per the instructions in +L + +=head1 RUNNING THE "CANNED" CATALYST TESTS + +There are a variety of ways to run Catalyst and Perl tests (for example, +C and C), but one of the easiest is with the +C command. For example, to run all of the tests in the C +directory, enter: + + $ prove --lib lib t + +The redirection used by the Authentication plugins will cause the +default C to fail. You can fix this by changing the line in +C that read: + + ok( request('/')->is_success, 'Request should succeed' ); + +to: + + ok( request('/login')->is_success, 'Request should succeed' ); + +So that a redirect is not necessary. Also, the C +and C default test cases will fail because of the +authorization. You can delete these two files to prevent false error +messages: + + $ rm t/controller_Books.t + $ rm t/controller_Logout.t + +As you can see in the C command line above, the C<--lib> option +is used to set the location of the Catalyst C 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 to comment out the C<-Debug> +plugin, it's generally easier to simply set the C +environment variable. For example: + + $ CATALYST_DEBUG=0 prove --lib lib t + +During the C and C tests, you might notice the +C warning message. To +execute the Pod-related tests, add C to the C +command: + + $ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib t + +If you omitted the Pod comments from any of the methods that were +inserted, you might have to go back and fix them to get these tests to +pass. :-) + +Another useful option is the C (C<-v>) option to C. It +prints the name of each test case as it is being run: + + $ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib -v t + +=head1 RUNNING A SINGLE TEST + +You can also run a single script by appending its name to the C +command. For example: + + $ CATALYST_DEBUG=0 prove --lib lib t/01app.t + +Note that you can also run tests directly from Perl without C. +For example: + + $ CATALYST_DEBUG=0 perl -Ilib t/01app.t + +=head1 ADDING YOUR OWN TEST SCRIPT + +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 +L module +is very popular for writing these sorts of test cases. This module +extends L (and therefore +L) to allow you to automate the action of +a user "clicking around" inside your application. It gives you all the +benefits of testing on a live system without the messiness of having to +use an actual web server, and a real person to do the clicking. + +To create a sample test case, open the C file in your +editor and enter the following: + + #!/usr/bin/perl + + use strict; + use warnings; + + # Load testing framework and use 'no_plan' to dynamically pick up + # all tests. Better to replace "'no_plan'" with "tests => 30" so it + # knows exactly how many tests need to be run (and will tell you if + # not), but 'no_plan' is nice for quick & dirty tests + + use Test::More 'no_plan'; + + # Need to specify the name of your app as arg on next line + # Can also do: + # use Test::WWW::Mechanize::Catalyst "MyApp"; + + 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) + # Note that in test scripts you send everything to 'http://localhost' + $_->get_ok("http://localhost/", "Check redirect of base URL") for $ua1, $ua2; + # Use title_is() to check the contents of the ... tags + $_->title_is("Login", "Check for login title") for $ua1, $ua2; + # 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'"); + # Use the form for user 'test02'; note there is no description here + $ua2->submit_form( + fields => { + 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 => 1}, "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'"); + # 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\">Logout", + "Both users should have a 'User Logout'") for $ua1, $ua2; + $ua1->content_contains("/books/form_create\">Create", + "Only 'test01' should 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->title_is("Book Created", "Book created title"); + $ua1->content_contains("Added book 'TestTitle'", "Check title added OK"); + $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"); + + # 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'); + # Use the final link to delete the last book + $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"); + + # 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"); + +The C 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 (for +example, regex-based matching). Consult the documentation for more +detail. + +B: For I vs. the "full application tests" approach used +by L, see +L. + +B The test script does not test the C and +C actions. That is left as an exercise for the reader +(you should be able to complete that logic using the existing code as a +template). + +To run the new test script, use a command such as: + + $ CATALYST_DEBUG=0 prove --lib lib -v t/live_app01.t + +or + + $ DBIC_TRACE=0 CATALYST_DEBUG=0 prove --lib lib -v t/live_app01.t + +Experiment with the C, C +and C<-v> settings. If you find that there are errors, use the +techniques discussed in the "Catalyst Debugging" section (Part 6) to +isolate and fix any problems. + +If you want to run the test case under the Perl interactive debugger, +try a command such as: + + $ DBIC_TRACE=0 CATALYST_DEBUG=0 perl -d -Ilib t/live_app01.t + +Note that although this tutorial uses a single custom test case for +simplicity, you may wish to break your tests into different files for +better organization. + +B If you have a test case that fails, you will receive an error +similar to the following: + + # Failed test 'Check we are NOT logged in' + # in t/live_app01.t at line 31. + # searched: "\x{0a}content; + +This will cause the full HTML returned by the request to be displayed. + + +=head1 SUPPORTING BOTH PRODUCTION AND TEST DATABASES + +You may wish to leverage the techniques discussed in this tutorial to +maintain both a "production database" for your live application and a +"testing database" for your test cases. One advantage to +L is that +it runs your full application; however, this can complicate things when +you want to support multiple databases. One solution is to allow the +database specification to be overridden with an environment variable. +For example, open C in your editor and +change the C<__PACKAGE__-Econfig(...> declaration to resemble: + + my $dsn = $ENV{MYAPP_DSN} ||= 'dbi:SQLite:myapp.db'; + __PACKAGE__->config( + schema_class => 'MyAppDB', + connect_info => [ + $dsn, + '', + '', + { AutoCommit => 1 }, + + ], + ); + +Then, when you run your test case, you can use commands such as: + + $ cp myapp.db myappTEST.db + $ CATALYST_DEBUG=0 MYAPP_DSN="dbi:SQLite:myappTEST.db" prove --lib lib -v t/live_app01.t + +This will modify the DSN only while the test case is running. If you +launch your normal application without the C environment +variable defined, it will default to the same C as +before. + + +=head1 AUTHOR + +Kennedy Clark, C + +Please report any errors, issues or suggestions to the author. The +most recent version of the Catalyst Tutorial can be found at +L. + +Copyright 2006, Kennedy Clark, under Creative Commons License +(L). +