Monday, February 18, 2008
I had a strange error occur in one of my rspec model unit tests today, and I wanted to document it here because my solution (which is a bit of a hack) is the opposite of what worked for other people.
I have a bunch of tests that check to make sure I'm validating various properties of a model. All of a sudden, I started having tests fail because the validations seemed to be adding the same error twice.
'User creation should require domain names to be unique' FAILED
expected 1 error on :domain, got 2
This error only occured when my full suite of tests ran. If I ran the unit test by itself (or even if I just ran only the model tests by themselves), it didn't happen. Unfortunately I didn't notice whatever it was I did to introduce this error, so I couldn't reverse it.
Several other people have encountered this problem, so I know it's because my tests are leaking state in some way. Somehow I am doing something during my tests that rspec isn't able to clean up. Usually it's because the tests are doing something weird with extra require or load statements, which causes multiple copies of the class to be loaded. Removing these statements usually works.
I had no such statements and so spent a while trying to sort this out. On a whim, I added a require statement to the top of the failing User model spec, and that fixed it:
require 'user'
I wish I had more time to investigate, because it makes me thing I don't know enough about Rails autoloading behavior, or Ruby's loading behavior.
If you're running into this same problem, I found these mailing lists threads to be useful:
Labels: rails, rspec, testing
Wednesday, January 16, 2008
I followed a tip from
David Chelimsky's blog and began running autotest with the -v flag for verbosity. When you first run it, you get a bunch of lines like this:
Dunno! spec/other_inbox_spec_helpers.rb
Dunno! app/views/layouts/main/_footer.erb
Which show all the files for which autotest doesn't have a mapping. With
ZenTest 3.8.0 out, it's easy to add mappings (which tell autotest which tests to run when a matching file changes) and exceptions (which tell autotest which files to ignore). You can also set up .autotest files for particular projects, or for your whole development machine (~/.autotest).
Here's an example of a per-project .autotest I use, sitting in the Rails root directory. I've got a custom spec helper and I want to rerun all my tests if this file ever changes:
Autotest.add_hook :initialize do |at|
%w{ domain_regexp perfdata coverage reports }.each { |exception| at.add_exception(exception) }
at.add_mapping(/spec\/app_spec_helper.rb/) do |_, m|
at.files_matching %r%^spec/(controllers|helpers|lib|models|views)/.*\.rb$%
end
end
And here's part of my site-wide .autotest file, mostly cribbed from David's blog, where I'm ignoring other kinds of cruft that pile up in projects. Also note the mapping for spec/defaults.rb, a file I commonly setup in my specs containing default parameters for different models.
Autotest.add_hook :initialize do |at|
%w{.hg .git .svn stories tmtags Rakefile Capfile README spec/spec.opts spec/rcov.opts vendor/gems autotest svn-commit .DS_Store }.each {|exception|at.add_exception(exception)}
at.add_mapping(/spec\/defaults.rb/) do |f, _|
at.files_matching %r%^spec/(controllers|helpers|lib|models|views)/.*\.rb$%
end
end
Labels: autotest, rails, rspec, testing