When Inheritance Doesn't Pay - Problems Testing Rails
The smallest things have the capacity to cause the most pain. Today's pain is caused by 5 characters, or to be more acurate, the absence of 5 characters.
super
Say it with me, super. Yep, super. Guess what Rails doesn't include in the setup of functional tests by default. You betcha, super. And the funny thing is, most of the time, it really doesn't matter. But sometimes, oh boy, yeah, sometimes it makes all the difference.
Given two test classes:
a_test.rb
b_test.rb
Would it surprise you that run seperately, both of these pass with flying colors, but run together, they result in abominable crashes? It sure surprised me.
The key to this dilemma should be pretty easy to find now that I have bloodied myself on the problem for you. Exactly, super. Add super to the setup and all our woes are cured. I haven't got the slightest idea what magic TestCase.setup is doing but without it, we bomb every time. Actually, I suspect inheritance isn't even the culprit here. Running a with itself twice exhibits the same behavior, with the same solution.
I suspect the error condition is related to the edge case of a test class completing then being loaded again but I'm not sure how to prove it. I'm guessing the 2nd time through, an action isn't take because Rails is convinced it already happened. But the moral is clear, super is your friend.
super
Say it with me, super. Yep, super. Guess what Rails doesn't include in the setup of functional tests by default. You betcha, super. And the funny thing is, most of the time, it really doesn't matter. But sometimes, oh boy, yeah, sometimes it makes all the difference.
Given two test classes:
a_test.rb
require File.dirname(__FILE__) + '/../test_helper'
class ATest < Test::Unit::TestCase
fixtures :empties
def setup
end
def test_fixture
empties(:first)
end
end
b_test.rb
require File.dirname(__FILE__) + '/a_test'
class BTest < ATest; end
Would it surprise you that run seperately, both of these pass with flying colors, but run together, they result in abominable crashes? It sure surprised me.
[~/projects/test]$ /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb" "test/unit/a_test.rb" Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader
Started
.
Finished in 0.115591 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
[~/projects/test]$ /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb" "test/unit/b_test.rb" Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader
Started
..
Finished in 0.157902 seconds.
2 tests, 0 assertions, 0 failures, 0 errors
[~/projects/test]$ /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb" "test/unit/a_test.rb" "test/unit/b_test.rb"
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader
Started
EEEE
Finished in 0.088633 seconds.
1) Error:
test_fixture(ATest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.[]
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:475:in `empties'
./test/unit/a_test.rb:10:in `test_fixture'
2) Error:
test_fixture(ATest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.-
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/transactions.rb:112:in `unlock_mutex'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:534:in `teardown'
3) Error:
test_fixture(BTest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.[]
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:475:in `empties'
./test/unit/a_test.rb:10:in `test_fixture'
4) Error:
test_fixture(BTest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.-
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/transactions.rb:112:in `unlock_mutex'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.2/lib/active_record/fixtures.rb:534:in `teardown'
2 tests, 0 assertions, 0 failures, 4 errors
[~/projects/test]$
The key to this dilemma should be pretty easy to find now that I have bloodied myself on the problem for you. Exactly, super. Add super to the setup and all our woes are cured. I haven't got the slightest idea what magic TestCase.setup is doing but without it, we bomb every time. Actually, I suspect inheritance isn't even the culprit here. Running a with itself twice exhibits the same behavior, with the same solution.
I suspect the error condition is related to the edge case of a test class completing then being loaded again but I'm not sure how to prove it. I'm guessing the 2nd time through, an action isn't take because Rails is convinced it already happened. But the moral is clear, super is your friend.