Automated testing needs a stable database environment in which to do its work. The contents of the development database will—and should—change on a regular basis as you tinker, try things out, and experiment to see just how well everything works. This is wonderful for a human development process, but that level of change is dreadful when a computer is testing an application. Once the testing framework is told which value to check for, it can’t choose another value because it knows someone else was playing with the data. In fact, if previous tests change the data, the order in which tests are conducted could itself become an issue, masking some bugs and falsely reporting others.
Rails provides this stable environment two ways. First, as noted earlier, it maintains a separate test environment, complete with its own database. Second, the testing environment expects that developers will define stable data, called fixtures, for use in that database. Every time a new test is run, the database is reset to that stable set of data. It’s a slow way to do things, but it’s extremely reliable.
Fixtures are written in YAML. You don’t need to know much about YAML to use and create them, however. Rails, in fact, has been creating fixtures along with scaffolding all along. If you check the test/fixtures directory of the courses and students application, you’ll see files named awards.yml, courses.yml, and students.yml. Their contents aren’t particularly exciting, though, as Example 12-1 demonstrates.
Example 12-1. The students.yml fixture file created by Rails
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html one: given_name: MyString middle_name: MyString family_name: MyString age: 1 grade_point_average: 9.99 start_date: 2008-06-27 two: given_name: MyString middle_name: MyString family_name: MyString age: 1 grade_point_average: 9.99 start_date: 2008-06-27
Each field has a value, set by the Rails generator to reflect its type, and there are two records, but you may want something more reflective of the data your application is likely to contain, like Example 12-2.
Example 12-2. A more realistic, though still brief, students.yml fixture
giles: given_name: Giles middle_name: Prentiss family_name: Boschwick date_of_birth: 1989-02-15 grade_point_average: 3.92 start_date: 2006-09-12 milletta: given_name: Milletta middle_name: Zorgos family_name: Stim date_of_birth: 1989-04-17 grade_point_average: 3.94 start_date: 2006-09-12 jules: given_name: Jules middle_name: Bloss family_name: Miller date_of_birth: 1988-11-12 grade_point_average: 2.76 start_date: 2006-09-12
It’s up to you whether you’d like the data to echo the development database, but somewhat meaningful data can be useful when you’re trying to find your way through results, especially failures.
If you try to run tests based on the generated fixtures and your migrations set constraints on which fields can be null, you’ll get a lot of mysterious errors. In SQLite, they suggest that your database and all of its tables are missing—even though they’re not. When using MySQL, the error message at least narrows things down to fields, but that still doesn’t explain why there’s a problem.
The scaffold fixtures may work for testing incredibly simple applications, but most of the time you’ll be much better off defining your own fixtures carefully.
There’s more you can do in upgrading fixtures than improving readability, however. The fixtures Rails created don’t know very much about relationships between models because the fixtures were generated before you told Rails about the relationship. So, for example, the generated fixture for awards looks like Example 12-3.
Example 12-3. The generated awards.yml fixture, without much real data
one: name: MyString year: 1 student_id: 1 two: name: MyString year: 1 student_id: 1
Rails knows that student_id
is
a number and gives it a value of 1, which should connect to a student,
although as you might have noticed in Example 12-1, the students.yml fixture didn’t include id
values. The database might start its
id
count at 1, or it might
not.
Fixture data isn’t validated before it’s loaded into the database. While this might conceivably offer more testing flexibility, you should never assume that fixture data will validate against the model until you’ve made certain that it does.
Example 12-4 shows a better way to create this fixture, taking advantage of the names in the student.yml file that was shown in Example 12-2.
Example 12-4. The awards.yml fixture, populated with semi-real data and links to students
# instead of computing student_id for each award and giving students # explicit id fields, we reference the student by the name of their # fixture skydiving: name: Sky Diving Prowess year: 2007student: giles
frogman: name: Frogman Award for Underwater Poise year: 2008student: jules
It’s important to note that the names of students used to make the
connections aren’t coming from the given_name
field. They’re the names that were
assigned to each student object in the fixture. The same thing applies
to the fixture, only it can actually refer to multiple students, not
just one. The original fixture, shown in Example 12-5, doesn’t even
specify any students for courses. Example 12-6, by contrast,
establishes relationships, using the names of the fixtures.
Example 12-5. The generated courses.yml fixture, with very little content
one: name: MyString two: name: MyString
Example 12-6. The courses.yml fixture, populated with sort of real data
# instead of making us write elaborate and fragile data structures, # the fixtures engine knows how to turn the 'students' list into a # collection of records to insert into the courses_students table. opera: name: Mathematical Opera students: giles, milletta # it's safest to quote strings, especially if they contain colons reptiles: name: "Reptiles: Friend or Foe?" students: giles, jules immoral: name: "Immoral Aesthetics" students: milletta
The fixtures setup is smart enough to establish the many-to-many connection between courses and students and build the necessary table, when given data like Example 12-6.
You can edit these files by hand, or you can generate fixtures
from the development database with the ar_fixtures
plug-in. You can find out more
about ar_fixtures
at http://railsify.com/plugins/8-ar_fixtures. You can even
create dynamic fixtures if you want, though that’s well beyond the
scope of this book.
Once you have your fixtures set up, you can try running rake test
.
You’ll probably get a lot of errors, because the tests themselves aren’t
well set up. A lot of errors is a normal place to start in testing,
however—it just means there’s a lot to fix!
When you run rake test
, Rails
will clone the structure (but not the content) of your development
database into the test database. It doesn’t run the migrations against the
test database directly, but it does check to make sure your database is
up-to-date with its migrations. If it isn’t, you’ll get a warning like:
You have 4 pending migrations: 20080627135838 CreateStudents 20080627140324 CreateCourses 20080627144242 CreateCoursesStudents 20080627150307 CreateAwards Run "rake db:migrate" to update your database then try again.
If you get major error messages that sound like your database can’t be found, as noted in the warning earlier, check your fixtures to ensure that every field your migrations said had to be there has an actual value.
Once you have the fixtures set up, it’s time to move on to the tests.