Rails: Are the Fixtures Valid?
A common pattern in my Ruby on Rails unit tests is to start with a valid instance of a model, make a change to the attribute I'm testing, then assert that the model instance is no longer valid.
test "weight is required" do
@prod_one = products(:one)
@prod_one.weight = nil
assert !@prod_one.valid?
end
Simple enough; I don't think we're treading new ground here. The problem is that this test assumes the fixture is valid. If the fixture isn't valid, the test will still pass, but it won't actually be testing anything. Yikes! Better to assert that the fixture is valid.
test "weight is required" do
@prod_one = products(:one)
assert @prod_one.valid?
@prod_one.weight = nil
assert !@prod_one.valid?
end
That's definitely better, but we've got a bunch of tests to write; it's going to be tiresome (and error-prone) to have to assert the validity of our fixtures in each test. Plus, we want to keep our tests laser focused on what they're actually testing. Why not just get it all out of the way at once?
setup do
@prod_one = products(:one)
end
test "fixtures are valid" do
assert @prod_one.valid?
end
test "weight is required" do
@prod_one.weight = nil
assert !@prod_one.valid?
end
Look at how tight that test is. There's nothing in there to distract you from immediately comprehending what's being tested. Truly a thing of beauty. There is still one thing that's bothering me though...
We're going to be testing our fixtures this way a lot -- at least once for each unit and functional test class. It's tiresome to actually have to write an entire test each time we want to do this. Why not just write a helper method so we can turn this into a one liner?
class ActiveSupport::TestCase
# some other stuff
def self.test_valid_fixture(*fixts)
fixts.each do |fixture|
test "@#{fixture} fixture is valid" do
fixture = instance_variable_get("@#{fixture}")
assert fixture.valid?,
"Fixture is invalid: #{fixture.errors.full_messages.join(', ')}"
end
end
end
end
Now we can be concise and expressive in our test class, which allows us to conserve our brainpower for where it's really needed.
class ProductTest < ActiveRecord::TestCase
setup do
@prod_one = products(:one)
@prod_two = products(:two)
end
test_valid_fixture :prod_one, :prod_two
test "weight is required" do
@prod_one.weight = nil
assert !@prod_one.valid?
end
end