Unit test quickie
October 23rd, 2007
Having your unit tests isolated is something to strive for. Why? because ideally, when you (or someone else) introduces a bug to the system, only a single unit test would fail, thus leading you to the source of the error quickly. If an error triggers hundreds of tests to fail, you’d have to spend a good time looking for the source of the error.
Mocks to the rescue, again. Do you have the following pattern recurring in your unit tests?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
describe Project, "(valid)" do it "is valid if it has a name and an owner" do Project.new(:name => 'Death Star II', :owner => 'moff.jerjerrod@ct.empire.mil').should be_valid end it "is NOT valid if it is missing name" do Project.new(:owner => 'moff.jerjerrod@ct.empire.mil').should_not be_valid end it "is NOT valid if it is missing owner" do Project.new(:name => 'Death Star II').should_not be_valid end it "can contain issues" do issue = build_an_issue(:summary => 'proton torpedo in the ventilation shaft might cause havoc', :severity => 2) proj = build_valid_project proj.issues << issue end def build_valid_project Project.new(:name => 'test project', :owner => 'project@man.ag.er') end ... end |
So do I. But there’s one tiny problem if you have lots of tests and the validation code gets more complex. If the validation fails, all the tests using build_valid_project could fail as the project returned by the method wouldn’t be valid anymore. So, time to flex your mocking skills again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
describe Project, "(valid)" do it "is valid if it has a name and an owner" do Project.new(:name => 'Death Star II', :owner => 'moff.jerjerrod@ct.empire.mil').should be_valid end it "is NOT valid if it is missing name" do Project.new(:owner => 'moff.jerjerrod@ct.empire.mil').should_not be_valid end it "is NOT valid if it is missing owner" do Project.new(:name => 'Death Star II').should_not be_valid end it "can contain issues" do issue = build_an_issue(:summary => 'proton torpedo in the ventilation shaft might cause havoc', :severity => 2) proj = build_valid_project proj.issues << issue end def build_valid_project returning Project.new do |proj| flexmock(proj, :valid? => true) end end |
Now the project returned by build_valid_project would seem like a valid project; even Rails would consider it valid all right. Which is a good thing, because we created the build_valid_project method in the first place to, ummm, quickly set up a valid project we can use in several specifications. But because we stubbed ActiveRecord::Base#valid?@ we would really have to test the idea of a valid project somewhere. That’s what we do in the first three tests (and would add more if we needed, but only once).
Conclusion: mocking stuff helps you keep your test assertions krhm, spec expectations more DRY.

Leave a Reply