Recipe 7.12. Testing Across Controllers with Integration Tests


Problem

You want to comprehensively test your application by simulating sessions that interact with your application's controllers.

Solution

Create an integration test that simulates a user attempting to view your application's authenticated reports:

$ ruby script/generate integration_test report_viewer             

This creates an integration test named report_viewer_test.rb in ./test/integration. Within this file, you define the sequence of events that you want to test by issuing simulated requests and making various assertions about how your application should respond.

Factor out recurring sequences of actions into private methods named for what they do, such as log_in_user and log_out_user:

test/integration/report_viewer_test.rb:

require "#{File.dirname(__FILE__)}/../test_helper" class ReportViewerTest < ActionController::IntegrationTest   fixtures :users   def test_user_authenticates_to_view_report     get "/reports/show_sales"     assert_response :redirect     assert_equal "You must be logged in.", flash[:notice]     follow_redirect!     assert_template "login/index"     assert_equal "/reports/show_sales", session["initial_uri"]       log_in_user(:sara)     assert session["user_id"]     assert_template session["initial_uri"].sub!('/','')     log_out_user     assert_nil session["user_id"]   end   private      def log_in_user(user)       post "/login", :user => { "username" => users(user).username,                                 "password" => users(user).password }       assert_response :redirect       follow_redirect!       assert_response :success     end     def log_out_user       post "/logout"       assert_response :redirect       follow_redirect!       assert_response :success     end end

Run the integration test individually from your application's root with:

$ ruby ./test/integration/report_viewer_test.rb             

or run all your integration tests with:

$ rake test:integration             

Discussion

Unlike unit and functional tests, integration tests can define test methods that exercise actions from multiple controllers. By chaining together requests from different parts of your application, you can simulate a realistic user session. Integration tests exercise everything from Active Record to the dispatcher; they test the entire Rails stack.

The solution's test method starts by requesting the show_sales method of the reports controller with get "/reports/show_sales". Notice that unlike functional tests, the get method in integration tests is called with a path string, not an explicit controller and action. Because the Reports controller has a before filter that requires users to be logged in, a redirect is expected and tested with assert_response :redirect. The contents of flash[:notice] is also tested: it must contain the correct reason for the redirect.

After following the redirect with follow_redirect!, assert_template "login/index" verifies that the login form was rendered. You should also make sure that the URI of the initial request is stored in the session hash (i.e., session["initial_uri"]).

You can make the integration test more readable and easier to write by creating private methods for groups of actions that are used more than once, such as logging in a user. Using the log_in_user method, you log in a user from the users fixture and assert that a user_id is stored in the session hash. Expect that once users are logged in, they'll be redirected to the page they initially tried to visit. You test for this by asserting that the template rendered is the same as the contents of session["initial_uri"].

Finally, we log the user out with log_out_user method and assert that the user session id has been cleared.

By default, methods called within integration tests are executed in the context of a single session. This means that anything stored in the session hash during the test method is available through the rest of the method. It's also possible to create multiple sessions simultaneously using the open_session method of the IntegrationTest class. This way you can test how multiple sessions may interact with one another and your application. The following is a modified version of the test method from the solution that tests two concurrent users authenticating and view reports.

require "#{File.dirname(__FILE__)}/../test_helper" class ReportViewersTest < ActionController::IntegrationTest   fixtures :users   def test_users_login_and_view_reports     sara = new_session(:sara)     jack = new_session(:jack)     sara.views_reports     jack.views_reports     sara.logs_out     jack.logs_out   end   private      module CustomAssertions       def log_in_user(user)         post "/login", :user => { "username" => users(user).username,                                   "password" => users(user).password }         assert_response :redirect         follow_redirect!         assert_response :success       end            def views_reports         get "/reports/show_sales"         assert_response :success       end            def logs_out         post "/logout"         assert_response :redirect         follow_redirect!         assert_response :success       end          end                def new_session(user)       open_session do |sess|         sess.extend(CustomAssertions)         sess.log_in_user(user)       end          end end

The new_session method creates session objects that mix in methods of the CustomAssertions module. The same method logs in the user passed in as a parameter by calling log_in_user as one of the mixed-in methods. The effect of this style of integration testing is that any number of sessions can interact. Adding the methods of the CustomAssertions module to each of these sessions effectively creates a domain specific language (DSL) for your application, which makes tests easy to write and understand after they're written.

See Also

  • Rails API documentation on IntegrationTest,

    http://api.rubyonrails.org/classes/ActionController/IntegrationTest.html



Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net