No One "Caught" It?

Posted on January 14, 2006 by Scott Leberknight

Apparently none of the <sarcasm>tons of people who read my blog</sarcasm> caught the fact that in my test failings part ii entry the test code is wrong. (I only caught it because I read that entry as I was writing abour validation in Rails.) If the line of code dao.findVisit(-9999L) executes without throwing any exception, then the test still passes! Just because the test method throws Exception and the try/catch only deals with HibernateObjectRetrievalFailureException does not mean the test fails when the findVisit method throws no exception at all. You still need to remember to call fail() if no exception is thrown, as the following shows.

public void testFindNonExistentVisit() throws Exception {
    try {
        dao.findVisit(-9999L);
        fail("findVisit should have thrown an exception!");  // you do need this line!
    }
    catch (HibernateObjectRetrievalFailureException ex) {}
}

So even though all the ExceptionAssertionTemplate code is more verbose (hey, it's what you expect from Java developers, right?) it still ensures that you don't forget to add the call to fail() in your test methods. I don't think it's that much more verbose, and since I apparently cannot even get my code correct in a blog entry, it's probably a good idea to use it, or else I'll keep forgetting to call fail() and have my tests passing when they should fail.

Supressing Validation in Rails On Fields That Are Not Required

Posted on January 14, 2006 by Scott Leberknight

I've been really busy since my last post, which was was back in October of last year. Time goes by too damn fast! Anyway, I've been mucking around with Ruby on Rails lately and was adding some validation to a model object. I had a bunch of fields that are not required, but if a value is entered they should be validated against a specific format. In my case email addresses and phone numbers. Normally to validate the format of a field in Rails you can simply write the following. (Note that EMAIL_FORMAT is a Regexp object created as a constant in the class in case you wonder where it came from.)

validates_format_of :email,
                    :with => EMAIL_FORMAT,
                    :message => 'must be a valid email address, e.g. a@b.com'

That is all well and good except for the minor problem that this will cause Rails to run validation on the email field regardless of whether it is empty or not. So even though the field isn't required, Rails will validate it and report an error back to the user. Not exactly what I had in mind.

So after some extensive Googling, I found some documentation and examples that use the :if option along with a Proc object, which I have heard of but don't really understand. I'll learn that later, as for now I simply want to make the validation work properly. So the following code will validate the email address, but only if the user actually entered a value.

validates_format_of :email,
                    :allow_nil => true,
                    :with => EMAIL_FORMAT,
                    :if => Proc.new {|c| not c.email.blank?},
                    :message => 'must be a valid email address, e.g. a@b.com'

So that's how you do it. You create a new Proc object with a block that checks whether the field you are validating is blank. Rails will only run validation on the email field if it's not blank. Note that I also used the allow_nil option to tell Rails that a nil value is OK. If the value is nil then validation is suppressed. But won't that take care of blank values sent in from a web form? Unfortunately Rails (at least by default) will not convert empty strings received from the HTTP request parameters to nil automatically. If it did, then the :if option would not be required in the validation. So if, for example, you sent a GET request with the query string "first_name=Scott&last_name=Leberknight&email=&cell_phone=&home_phone=", Rails would assign empty strings to email, cell_phone, and home_phone request parameters. Or perhaps I should say it simply takes the request parameters as they are sent by the browser and assigns the values sent by the browser. This behavior is the same for POST requests when you don't fill out a value for things like text fields and text areas, select lists, etc. in forms. The crappy thing about empty strings is that they'll be stored in your database as empty strings rather than as null values. That's something to research later, as I want null values instead of empty strings in my database.