Integration testing is the most complicated testing Rails supports directly. It tests complete requests coming in from the outside, running through routing, controllers, models, the database, and even views. Rails does not generate any integration tests by default, as creating them requires detailed knowledge of the complete application and what it is supposed to do. Integration tests are stored in tests/integration and look much like the classes for other kinds of tests. They call similar methods and also make assertions, but the assertions are different and the flow can cover multiple interactions, as Example 12-15 demonstrates.
Example 12-15. An integration test that tries adding a student
require 'test_helper' # Integration tests covering the manipulation of student objects class StudentsTest < ActionController::IntegrationTest def test_adding_a_student # get the new student form get '/students/new' # could be new_students_path # check there are boxes to put the name in # trivial in our case, but illustrates how to check output HTML assert_select "input[type=text][name='student[given_name]']" assert_select "input[type=text][name='student[family_name]']" assert_difference('Student.count') do post '/students/create', :student => { :given_name => "Fred", :family_name => "Smith", :date_of_birth => "1999-09-01", :grade_point_average => 2.0, :start_date => "2008-09-01" } end assert_redirected_to "/students/#{assigns(:student).id}" follow_redirect! # for completeness, check it's showing some of our data assert_select "p", /Fred/ assert_select "p", /2008-09-01/ end end
Instead of calling the create
method directly, as the functional tests would do, test_adding_a_student
starts by using the
get
method—with a URI fragment rather
than a function name—to retrieve the form needed for adding a
student.
Next, the method examines that form with assert_select
, one of Rails’ methods for testing HTML documents to see if
they contain what you expect them to contain. In the first of those two
statements, assert_select
tries to
match the pattern:
input[type=text][name='student[given_name]']
That would be an input
element
with a type
attribute set to text
and a name
attribute set to student[given_name]
. (The single quotes are
necessary to keep the [
and ]
from causing trouble with the match pattern
syntax.) The form should match that, as it contains:
<input id="student_given_name" name="student[given_name]" size="30" type="text" />
Once Rails has performed those assertions, it moves to actually
submitting a new student. There’s no way (yet) for Rails to actually
fill in the form and press the submit button, but the test does the
next best thing, issuing a POST request that reflects what the form
would have done, from inside of an assert_difference
call that looks for an added student:
assert_difference('Student.count') do post '/students/create', :student => { :given_name => "Fred", :family_name => "Smith", :date_of_birth => "1999-09-01", :grade_point_average => 2.0, :start_date => "2008-09-01" } end
Again, the call is to a URL, not to a method name, though this
post
call includes parameters
designed to reflect the structure that would be returned by the form
Rails generated. The page showing this student, Fred Smith, should come
back from Rails for display through a redirect, so the next assertion
watches for that:
assert_redirected_to "/students/#{assigns(:student).id}"
The assertion can grab the id
value
for the new student, whatever it is, from the controller, using the
all-powerful assigns
method. If it gets sent somewhere other than it expects,
it will report failure.
The next call is fairly self-explanatory:
follow_redirect!
There’s one last step needed here: checking that response to see
if it reflects expectations. Following the redirect lets the test
continue to the final part of the interaction, in which Rails shows off
the newly created student. Here, the test uses more assert_select
statements in a slightly
different syntax:
assert_select "p", /Fred/ assert_select "p", /2008-09-01/
When given a string and a regular expression as arguments, assert_select
will look for elements of the
type given in the string (here, p
)
that contain values matching the expression. Appendix C has more details
on regular expressions, but the first of these is just the string
Fred
, while the other is an escaped
version of 2008-09-01
. These are, of
course, the values that the test set earlier, and they should appear in
the document. Will they?
$ rake test:integration
(in /Users/simonstl/Documents/RailsOutIn/current/code/ch12/students005)
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test
"/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake/rake_test_loader.rb"
"test/integration/students_test.rb"
Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake/rake_test_loader
Started
.
Finished in 0.368834 seconds.
1 tests, 7 assertions, 0 failures, 0 errors
It all worked.
Creating useful integration tests is difficult. It requires plotting a path through your application, deciding which pieces are relevant, and which are not. As your application grows in complexity and interdependence, they may become critical, though smaller applications can often do without them for a long while.