-
4 April 2011
valid_attribute on Github
I’ve been writing model tests for years. Like most I started out with Shoulda which was great. As I moved over to RSpec thankfully Shoulda moved over as well.
After a while I began to hit some walls with Shoulda, specifically when I wanted to use some of the matchers for non-ActiveRecord models. I previously wrote a gem called remarkable_mongoid that provided relation and validation matchers for Mongoid. I originally wanted to use Shoulda as it was my library of choice but the ActiveModel validators were part of the Shoulda ActiveRecord set. So I refactored Shoulda to separate the ActiveModel validator matchers from the ActiveRecord validator matchers. Unfortunately it never got any attention. (I’ve since deleted my local repo) I decided to go with Remarkable which had the validators properly separated.
My views on model testing have shifted since I wrote remarkable_mongoid. Specifically I think relation matchers are crap. At the time I thought the same of validation matchers. My problem with validation matchers wasn’t with the necessity of them but how they were being implemented. Shoulda and Remarkable had similar APIs for how their matchers worked. Their validation matchers feel too tightly coupled with the implementation of the validations themselves. I would find myself writing a spec and a model like this:
I don’t know about you, but this feels like a unnecessary duplication of effort. And this morning I just about had it. So I spiked some code and came up with valid_attribute The same validations from above would work like this:
Now when comparing these two examples it might not be immediately obvious on the advantage of valid_attribute. But lets look at it from developing with BDD.
I want to create a Company model, and I’m going to test first. I’ll start by describing valid and invalid criteria. Then add the necessary validations to make the tests pass.
But now I need to add some more validations to the password attribute.
So we don’t add any more matchers, only update the acceptable values. When I am testing my models I care about asserting that certain data is valid and other data is invalid. valid_attribute makes this dead simple to do just that.
I also do not think having a #.not_with method on the matcher makes any sense. RSpec already has should_not. And it should be used rather than passing false values to a matcher that expects to return true.