I avoid writing fixtures for testing. They are cumbersome to write and management (particularly after the fact) is a hassle. When testing a model’s validations I want to work with a generally static model that only changes one or two attributes which illustrate the test condition I am working on. To this end I followed Luke Redpath’s blog post on BDD and models in particular that uses a more behavior driven approach to testing a model and it’s attributes.
As such in my rails projects now I like to use this Hash extension when defining the validation rules for my model.
module HashExtension
def except(*keys)
self.reject { |k,v|
keys.include? k.to_sym
}
end
end
And then I can define a model’s allowed behavior using RSpec. For example:
describe "a test model" do
def valid_model_attr
{ :name => "name", :age => 21}.extend(HashExtension)
end
it "should be valid" do
Model.new(valid_model_attr).should be_valid
end
it "should be invalid without name" do
model = Model.new(valid_model_attr.except(:name))
mode.should_not be_valid
model.errors.on(:name).should_not be_nil
end
it "should be invalid if age is under 21" do
model = Model.new(valid_model_attr.except(:age))
model.age = 20
mode.should_not be_valid
model.errors.on(:age).should_not be_nil
end
end
I can add as many tests as I want without defining several test specific fixtures which need to be updated when the validation rules for a model change. Using an attribute hash and except I can update one hash.
This technique gets a little more complicated when testing a model with a has_many / belongs_to relationship but I recently had success with that on this site I developed (www.clowndown.net). In that case I defined a test hash a such:
before(:each) do
@team = Team.new
end
def valid_team
{
:name => "team name",
:contact_number => "call me here!",
:people => [valid_person, valid_person],
:knowledge_of_chicago => "pretty good",
:why_will_win => "might get lucky",
:relationship => "great"
}.extend(HashExtension)
end
def valid_person
person = Person.new
person.attributes = {
:email => "email@email.com",
:name => "My Great Name",
:shirt_size => "L",
:hometown => "Chicago",
:team => @team,
:occupation => "occupation"
}
person
end
And I was able to write specs like this that used an object relation that focused on testing of the behavior of the has_many / belongs_to relation.
it "is invalid with 1 person" do
@team.attributes = valid_team.except(:people)
@team.people = [valid_person]
@team.should_not be_valid
@team.errors.on(:people).should_not be_nil
end