Cookin' with Ruby on Rails - Designing for Testability
Pages: 1, 2, 3, 4, 5, 6, 7

And we'll change the assertions in the test_must_have_a_valid_name and test_long_names methods, so our category test case looks like ...

test case with assert_false
Figure 38

And we run our tests again to make sure we get the same results...

test results with new helper method
Figure 39

Paul: Excellent! So that wraps it up for Categories, right?

CB: For the Unit tests it does. We're caught up on our testing of the Category model's currently defined functionality. Let's go ahead and do the same for our Recipe model. I'll put the same validations we used in the category model into the recipe model (app\models\recipe.rb), only using the appropriate field name.

validates_presence_of :title
validates_length_of :title, :in => 1..100

And then I'll add the new methods to recipe_test.rb, again changing name to title in all the appropriate places. I also need to remember to add the title when I create a new recipe in the test_create_and_destroy method, like we did with categories. So now the recipe test case looks like...

require File.dirname(__FILE__) + '/../test_helper'

class RecipeTest < Test::Unit::TestCase
  fixtures :recipes

  def test_read_and_update
    rec_retrieved = Recipe.find_by_title("pizza")
    assert_not_nil rec_retrieved
    rec_retrieved.title = "pie"
    changed_rec = Recipe.find_by_title("pie")
    assert_not_nil changed_rec
    unwanted_rec = Recipe.find_by_title("pizza")
    assert_nil unwanted_rec

  def test_create_and_destroy
    initial_rec_count = Recipe.count
    new_rec = => "something new")
    new_rec.category_id = 1
    assert_equal(initial_rec_count + 1, Recipe.count)
    assert_equal(initial_rec_count, Recipe.count)

  def test_must_have_a_valid_title
    rec_with_title = => "something new")
    rec_with_title.category_id = 1
    rec_with_no_title =
    rec_with_no_title.category_id = 1
    rec_with_blank_title = => '   ')
    rec_with_blank_title.category_id = 1

  def test_long_titles
    partial_title = ''
    'a'.upto('y') {|letter| partial_title << letter}
    rec_with_borderline_title = => (partial_title * 4))
    rec_with_borderline_title.category_id = 1
    assert_equal(100, rec_with_borderline_title.title.size)
    rec_with_too_long_title = => ((partial_title * 4) << 'z'))
    rec_with_too_long_title.category_id = 1
    assert_equal(101, rec_with_too_long_title.title.size)

  def teardown
    recipes = Recipe.find(:all)
    recipes.each do |this_recipe|

CB: And when we run it...

now testing the full recipe unit test
Figure 40
Paul: Ta Da! ;-)

CB:Catchin', ain't it? ;-)

Paul: I gotta say, it definitely is. And I'm really looking forward to doing the Functional and Integration tests, so I hate to do this, but I gotta run right now. I've got a project status meeting with Boss and the Customer groups. Ought to just call these meetings the Beat on Paul meetings ;-p. Before I go, though, you said you were going to send me off with a reading list.

CB: You bet. Did you pick up the "Agile Web Development with Rails" book yet? That's got a chapter on testing in it. There's another good book you should pick up called "Rails Recipes." It's by Chad Fowler and it's got several useful testing recipes in it. There's a real good online tutorial called "Testing the Rails" at And there's a fellow by the name of Bala Paranj who maintains a list of links to other online resources for testing Ruby on Rails at That should be enough to get you started ;-)

Next time we get together, we'll put together the Functional and Integration tests we need to get ready for our next meeting with Boss.

As Paul exits, CB can't help but think, "This could be the start of some really positive change around here!"

Articles in this series

Bill Walton is a software development/project management consultant/contractor.

Return to O'Reilly Ruby.