[rspec-users] have_one and have_present

nicholas a. evans nick at ekenosen.net
Mon May 7 19:32:28 EDT 2007

On 5/7/07, nicholas a. evans <nick at ekenosen.net> wrote:
> validates_format_of (or other similarly complicated validations, such
> as using :if or :unless) is really where I have my biggest problem.

Sorry that my last email was such a long rant.  I'll try to restate the
important part more clearly, so you can ignore the other email.  ;-)

As an example, let's try BDD with email address validation.
We'll start with something simple like:
  model.should validate_format_of(:email, /\w+@\w+\.\w+/)
and then add this to the model, to make the spec pass:
  validate_format_of :email, :with => /\w+@\w+\.\w+/

Oh, that was rather naive; "foo bar blatz at test.com" is still valid.
Okay then, that old spec line was wrong; let's change it to:
  model.should validate_format_of(:email, /^\w+@\w+\.\w+$/)
and then change the model to:
  validate_format_of :email, :with => /^\w+@\w+\.\w+$/

Strange, this feels like I'm copying and pasting from my spec to my code!

Unfortunately, "foo+bar at test.com" isn't considered valid, nor is
"foo at test.domain.com".  "foo_bar at test.com" is still valid, but
"foo-bar at test.com" is not.  And so on, ad-infinitum, except that the specs
provide no fall-back guarantee that something that used to work will still
work, because I'm completely rewriting the spec at each step along the way.
This testing style feels rather brittle, and I'm still only using
ActiveRecord.  At some point, I think that I'd give up on TDD/BDD, and just
write the code first, then copy it back to the validation spec.

So, what happens when I want to say this?
  validate_format_of :with => cryptic_email_regex,
                     :unless => lambda { |user| user.type == "local" }

These are very simple regular expressions, and very simple business rules,
but they are already non-trivial and nuanced enough that the validation
method that is called is unimportant, and simple examples of what is valid
or invalid are far less confusing than a cryptic but powerful regex.

What I prefer to do: first I would add a simple example of a valid value,
and make sure that works prior to having any validations in place.  Then I'd
add a simple example of an invalid value, run the code, see that it fails,
then add the simplest validation necessary to pass the specs.  Then I'd add
another example, see it fail, and make it work.  I'd look closely at my
implementation to see if there are any edge-cases and then I'd make examples
of them to see if any of them fail, and I'd make them work.  And so on.
When the business requirements change, most likely I'll not need to change
any examples, I'll just need to add another.  At the end, I might wind up
with something similar to the following (another validation DSL attempt):

valid_model.should validate(:with_message => "Unacceptable email address.") do
  valid :email => "foo at bar.com"
  valid :email => "foo+extension at bar.com"
  valid :email => "foo-bar at test.com"
  valid :email => "foo-bar at test.domain.com"
  invalid :email => "anything at domain.we.dont.like"
  invalid :email => "no spaces please foo at bar.com"
  invalid :email => "@no.username.com"
  invalid :email => "only_tld at com"
  invalid({:email => ""}, :with_message => "Should provide email address")
  valid :email => "", :status => "registration incomplete"
  valid :email => "", :user_role => "fake"
  valid :email => "", :user_role => "admin"
  valid :email => "username", :type => "local"
  valid :email => "username at local.domain", :type => "local"
  invalid({:email => "username at domain.com", :type => "local"},
          :with_message => "local users must use a local username")

Please ignore whether or not the above validations are reasonable.  My
imaginary business analyst tells me they are all very important.  The level
of complexity should be reasonable, despite my bad examples. :-)

This style feels much more compatible with TDD/BDD, and far less brittle.
I also think that it is much clearer and more communicative.
It just isn't as concise.

But then again, it might be clear from my rambling emails that I don't place
enough value on being concise.  ;-)


More information about the rspec-users mailing list