Flylib.com

Books Software

 
 
 

Recipe 7.6. Interactively Testing Controllers from the Rails Console


Recipe 7.6. Interactively Testing Controllers from the Rails Console

Problem

You want to test the behavior of your application's controllers in real time, from the Rails console.

Solution

Start up a Rails console session with the ./script/console command from your application root. Use any of the methods of ActionController::Integration::Session to test your application from within the console session:

$

ruby script/console

Loading development environment.
>>

app.get "/reports/show_sales"

=> 302
>>

app.response.redirect_url

=> "http://www.example.com/login"
>>

app.flash

=> {:notice=>"You must be logged in to view reports."}
>>

app.post "/login", :user => {"username"=>"admin", "password"=>"pass"}

=> 302
>>

app.response.redirect_url

=> "http://www.example.com/reports/show_sales"

Discussion

By calling methods of the global app instance, you can test just as you would in your integration tests, but in real time. All methods available to integration tests are available to the app object in the console. By passing any nonfalse value to the application, you can create unique instances of ActionController::Integration::Session and assign them to variables . For example, the following assignments create two session objects (notice the unique memory addresses for each):

>> sess_one = app(true)
=> #<ActionController::Integration::Session:0x40a95e88 @https=false,
...
>> sess_two = app(true)
=> #<ActionController::Integration::Session:0x40a921c0 @https=false,
...

The new_session method does the same thing as app(true) but takes an optional code block that can be used to further initialize the session. Here's how to create a new session instance that mimics the behavior of an HTTPS session:

>> sess_three = new_session { s s.https! }
=> #<ActionController::Integration::Session:0x40a6dc44 @https=true, ...

For added feedback, you can enter commands in the console session while watching the output of ./script/server in another window. The following is the resultant output of invoking the app.get "/reports/show_sales" method from the console:

Processing ReportsController#show_sales 
     (for 127.0.0.1 at 2006-04-15 17:46:26) [GET]
  Session ID: 9dd6a4e3c591c484a243d166f123fd10
  Parameters: {"action"=>"show_sales", "controller"=>"reports"}
Redirected to https://www.example.com/login
Completed in 0.00160 (624 reqs/sec)  
     302 Found [https://www.example.com/reports/show_sales]

Assertions may be called from the console session objects but the output is a little different from what you may be used to. An assertion that doesn't fail returns nil , while those that do fail raise the appropriate Test::Unit::AssertionFailedError .

See Also

  • Section 3.5"



Recipe 7.7. Interpreting the Output of Test::Unit

Problem

You've diligently created unit tests for your application, but you're puzzled by what happens when you run the tests. How do you understand the output of Test::Unit ? How do you find which tests passed and failed?

Solution

Here are the results from running a small test case. The output shows that two tests passed, one failed, and one produced an error:

$

ruby test/unit/employee_test.rb

Loaded suite unit/employee_test
Started
.F.E
Finished in 0.126504 seconds.

  1) Failure:
test_employee_names_are_long(EmployeeTest) [unit/employee_test.rb:7]:
<"Nancy"> expected to be =~
</^\w{12}/>.

  2) Error:
test_employee_is_from_mars(EmployeeTest):
NoMethodError: undefined method 'from_mars?' for #<Employee:0x40763418>
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1498:in 
                                                                      'method_missing'
    unit/employee_test.rb:22:in 'test_employees_is_from_mars'

4 tests, 5 assertions, 1 failures, 1 errors

Even without seeing the test suite, we can get a feel for what happened . The .F.E message summarizes what happened when the tests ran: the dots indicate tests passed, F indicates a failure, and E indicates an error. The failure is simple: application should reject employees with a first name less than 12 character long, but it evidently doesn't. We can also tell what the error was: the test suite evidently expected the Employee class to have a from_mars? method, which wasn't there. (It's an error rather than a failure because the application itself encountered an errora call to a missing methodinstead of returning an incorrect result.) With this knowledge, it should be easy to debug the application.

Discussion

The order of the results in .F.E don't correspond to the order of the test method definitions in the class. Tests run in alphabetical order; the order in which tests run should have no effect on the results. Each test should be independent of the others. If they're not (if the tests only succeed if they run in a certain order), the test suite is poorly constructed . The output of one test should not impact the results of any other tests.

Notice that using descriptive test names makes it a lot easier to analyze the output. A name that shows just what a test is trying to accomplish can only help later, when the memory of writing the test fades.

See Also

  • Section 7.24"