Wednesday, June 18, 2008

Rails: Dynamic Test Fixtures

One of the clearest virtues of the Rails framework is the powerful testing support. Each generated model is created along with its unit test and test fixture. Likewise, each generated controller is created along with its functional test. Among others, test fixtures have proven to be the most handy testing feature introduced by Rails.

Rails test fixtures are named preset database records, written in YAML, that allow you to define the state of the database before each test case is run. Also, being named, a fixture can be accessed as a model instance through its name.

Well, old news I know. What I am introducing here is an extended feature of Rails fixtures, called "dynamic fixtures". Let's describe it with an example. Assume that we have a model called 'customer' with the following fixture:
bob:
name: Bob
email: bob@looney.com
appointment_date: 2008-07-01

Now assume that we need to write some code that does some logic on customers that have appointments today.
if customer.appointment_date == Date.today
...
Obviously, Any code that tests this logic using the given form fixtures, will succeed only on 2008-07-01, and fail otherwise. What we really want is to declare a fixture whose appointment_date is always set today. Well, cheer up, dynamic features allow you to declare fixtures like the following:
<% TODAY = Date.today %>

today_customer:
name: bob
email: bob@looney.com
appointment_date: <%= TODAY %>
not_today_customer:
name: alice
email: alice@looney.com
appointment_date: <%= TODAY.advance(:days => 10) %>
Using this dynamic fixture, tests will always run successfully without any need to change the fixtures every time tests are run.

Another well known situation where a dynamic fixture is needed is when we need to save in the database values that are computed in runtime. A typical example is when we save a hashed version of users passwords instead of plain ones. we compute the hashed password in runtime. The fixture can look like that:
Bob:
username: bob
password: <%= User.hash('1234') %>
Like this, we store a valid hashed version of the plain password '0000'. Handy, huh?


No comments: