The more time I spend writing tests, the more I learn and start to develop opinions about how to test certain things. This post is the first in what may be several attempts of mine to look at how to improve upon what may be a common way people try to test certain scenarios. These aren't meant to be revelations in design or anything, more so just a reminder to avoid some clunky tests here and there. So to start:
Validations & Associations
This is the type of test I really don't like:
it "should require username" do
user = User.new(all_attrs_but_username)
assert user.invalid?
assert user.errors[:username].any?
user.username = "foo"
assert user.valid?
end
This can vary a bit, but the gist is there. The "check without the attr, set the attr, then check again" approach feels extremely clunky to me, and requires so much work for what is so little work on the other side in the production code. Would you want one of these tests for every attribute on every model? I don't. As an alternative, just use matchers:
it { should validate_presence_of(:username) }
The one thing this obviously implies is that you should be writing your tests in a framework that supports this, or you should include a gem to help you. The example above uses shoulda:
https://github.com/thoughtbot/shoulda
Again, this feels obvious, but it seems like people still default to just using plain old straight up Test::Unit type tests as if there is something stopping them from making their tests cleaner. If you aren't using a syntax like Rspec or Shoulda, I think you're making your life worse than it needs to be.
Once you start using this, you can use the same simple syntax to check slightly more complex validation behavior, and also associations:
it { should_not allow_value("a"*3).for(:title) }
it { should allow_value("a"*4).for(:title) } #length tests
it { should have_many(:comments).dependent(:destroy) }
In my opinion, if you're putting too much work into these types of tests (like in the first example), you're starting to test Rails. If my model has the right validation or association parameters supplied, I trust Rails to handle the rest. And if you don't trust Rails, you most likely have some tests at a higher level that actually make use of these objects which should provide additional validation that things are setup at the lowest level correctly.