I’m not sure how much test data I should be using in my specs. I’m
writing specs for the Property model in my Rails app. Its “address”
attribute is going to be validated with this regex:
/\A\d+[a-z]? [-’, a-z]{2,128}\Z/i
At the moment, my plan is to spec out the following possibilities. A
property is invalid if its address:
doesn’t begin with a digit;
is shorter than 2 characters;
is longer than 128 characters;
I should also test that certain characters, such as ! @ # $ etc,
invalidate a property.
However, all of that seems like blacklisting, and achieves poor
coverage of the regular expression.
Should I create a list of valid and invalid addresses to test against
the regex? That seems like a decent idea, but seems synonymous with
fixtures. What would you guys recommend?
I should also test that certain characters, such as ! @ # $ etc, invalidate
a property.
However, all of that seems like blacklisting, and achieves poor coverage of
the regular expression.
Should I create a list of valid and invalid addresses to test against the
regex? That seems like a decent idea, but seems synonymous with fixtures.
What would you guys recommend?
I might do something like the following…
describe Property, “email validations” do
[“[email protected]”, “can’t start with a digit”,
“[email protected]”, “can’t end with a digit”
].in_groups_of(2) do |email, description|
it description do
prop = Property.new :email => email
prop.should_not be_valid
prop.should have(1).error_on(:email)
end
end
end
Hi Zach. That’s a great way of iterating over test data. Do you have any
suggestions for how much test data to use?
I’d probably start with the first invalid email/description that I can
think of. Perhaps it can’t start with a digit. Then I’d update the
regexp to reflect that. Next I’d add a second invalid
email/description and update the regexp to reflect that. Rinse and
repeat until your regexp is where you need it. This should help you
not add redundant emails which test the same thing. When you get to
the case where you allow from 2 up to 128 characters, you could just
test those two edge cases (rather than emails which 2 characters, 3
characters, 4 characters, … 128 characters.
Hi Zach. That’s a great way of iterating over test data. Do you have any
suggestions for how much test data to use?
I’d probably start with the first invalid email/description that I can
think of. Perhaps it can’t start with a digit. Then I’d update the
regexp to reflect that. Next I’d add a second invalid
email/description and update the regexp to reflect that. Rinse and
repeat until your regexp is where you need it.
This is nit-picky, I realize, but saying “until your regexp is where
you need it” suggests that you’ll know when you get there by looking
at the regexp. I’d suggest that you know when you get there by looking
at the code examples and feeling confident that you’ve covered all of
the edge cases.
This should help you
not add redundant emails which test the same thing. When you get to
the case where you allow from 2 up to 128 characters, you could just
test those two edge cases (rather than emails which 2 characters, 3
characters, 4 characters, … 128 characters.
If the boundaries are 2 and 128, I would test 1,2,3,127,128,129 - the
boundary and either side of it. Might seem like overkill, but I’ve
seen a bug or two make its way to production because the boundary
wasn’t clearly understood.
Each of the “can …” examples are proper addresses in the sense that
they’re variations of:
“123 A Street With A Long Name”
“123B Maple Ave”
“123 O’Connor Street”
etc
One thing that I didn’t do is write examples for each invalid
character. That’d be too hairy and verbose. Instead, I picked a few,
and wrote examples for them, as you can see above (Eg: “can’t have an
&”).
One thing that I didn’t do is write examples for each invalid character.
That’d be too hairy and verbose. Instead, I picked a few, and wrote examples
for them, as you can see above (Eg: “can’t have an &”).
If you want to (not necessarily advising this, but I’ve seen it done)
you
can do this:
[‘!’,‘(’,‘)’,‘&’].each do |char|
it “can’t have #{char}” do
…
end
end
That makes the output very verbose, but the spec file is easy to grok.
Good point. Thinking about it again, there’s probably not all that
much need to prevent addresses from having symbols such as !, &, ",
etc. If people really want to put them in, why not?
Does your office actually not have a proper street number?
-Nick
That makes the output very verbose, but the spec file is easy to grok.
Hi David. I ended up creating valid and invalid test data, as well as a
method to iterate over them and write the descriptions and examples for me.
You can have a look at it at http://pastie.org/261175 . If you have any
suggestions for improvements, I’d love to hear them.
Hey Nick,
I can definitely see some value in this, though there are a few things
that I’d do differently. Have a look at http://pastie.org/261642 and
let me know if you have any questions/comments. The only thing I’m not
sure about is the actual method name - it’s not speaking to me, but
I’m at a loss for a better one. Maybe after some coffee …
Hi David. I ended up creating valid and invalid test data, as well
as a method to iterate over them and write the descriptions and
examples for me. You can have a look at it at http://pastie.org/
261175 . If you have any suggestions for improvements, I’d love to
hear them.
Thanks for taking a look and making some suggestions, David. Much
appreciated. I’ve incorporated some of them into the method.
The method’s name was “describe_property_attributes” because it was
being used to describe the Property (as in “real estate”) model in my
application. I’ve just finished abstracting it out so that it can now
be used with any model class. If you’re interested, I’ve pasted it at http://pastie.org/261829
If you want to (not necessarily advising this, but I’ve seen it
done) you can do this:
[‘!’,‘(’,‘)’,‘&’].each do |char|
it “can’t have #{char}” do
…
end
end
That makes the output very verbose, but the spec file is easy to grok.
Hi David. I ended up creating valid and invalid test data, as well as
a method to iterate over them and write the descriptions and examples
for me. You can have a look at it at http://pastie.org/261175 . If you
have any suggestions for improvements, I’d love to hear them.
I started using a set of methods to deal with this problem from code in
the RadiantCMS(http://radiantcms.org/) project.
I stopped using it after a while finding the tests did not read well.
Your method improves on Radiant’s which has tempted me to start testing
models like this again.
Thanks for taking a look and making some suggestions, David. Much
appreciated. I’ve incorporated some of them into the method.
The method’s name was “describe_property_attributes” because it was
being used to describe the Property (as in “real estate”) model in my
application. I’ve just finished abstracting it out so that it can now
be used with any model class. If you’re interested, I’ve pasted it at http://pastie.org/261829
Thanks for the vote of confidence, Joseph! I have two other methods that go
alongside #describe_model_attribute which you might find useful. I’m in the
process of wrapping them up into a plugin. Until that’s finished, I’ve
pasted them at http://pastie.org/262508 and http://pastie.org/262509 .
Hey Nick - I’m not clear why you need these, given that you already
have the describe_model_attributes macro, which you can use to handle
nils and will give you a meaningful error if there is a problem with
your factory. What’s your goal here?