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 ...

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

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"
assert rec_retrieved.save
changed_rec = Recipe.find_by_title("pie")
assert_not_nil changed_rec
unwanted_rec = Recipe.find_by_title("pizza")
assert_nil unwanted_rec
end
def test_create_and_destroy
initial_rec_count = Recipe.count
new_rec = Recipe.new(:title => "something new")
new_rec.category_id = 1
new_rec.save
assert_equal(initial_rec_count + 1, Recipe.count)
new_rec.destroy
assert_equal(initial_rec_count, Recipe.count)
end
def test_must_have_a_valid_title
rec_with_title = Recipe.new(:title => "something new")
rec_with_title.category_id = 1
assert rec_with_title.save
rec_with_no_title = Recipe.new
rec_with_no_title.category_id = 1
assert_false rec_with_no_title.save
rec_with_blank_title = Recipe.new(:title => ' ')
rec_with_blank_title.category_id = 1
assert_false rec_with_blank_title.save
end
def test_long_titles
partial_title = ''
'a'.upto('y') {|letter| partial_title << letter}
rec_with_borderline_title = Recipe.new(:title => (partial_title * 4))
rec_with_borderline_title.category_id = 1
assert_equal(100, rec_with_borderline_title.title.size)
assert rec_with_borderline_title.save
rec_with_too_long_title = Recipe.new(:title => ((partial_title * 4) << 'z'))
rec_with_too_long_title.category_id = 1
assert_equal(101, rec_with_too_long_title.title.size)
assert_false rec_with_too_long_title.save
end
def teardown
recipes = Recipe.find(:all)
recipes.each do |this_recipe|
this_recipe.destroy
end
end
end
CB: And when we run it...

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 http://manuals.rubyonrails.com/read/chapter/20. 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 http://bparanj.blogspot.com/2006/11/resources-for-testing-ruby-on-rails.html. 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
- Cookin' with Ruby on Rails - More Designing for Testability by Bill Walton
- Cookin' with Ruby on Rails - May by Bill Walton
- Rolling with Ruby on Rails Revisited by Bill Walton and Curt Hibbs
Bill Walton is a software development/project management consultant/contractor.
Return to O'Reilly Ruby.
Showing messages 1 through 5 of 5.
-
database.yml
2008-06-26 14:32:11 wonger [View]
-
Small typo page 5
2007-08-15 20:55:08 ezosoro [View]
Posting this comment to help anyone who had a problem on page 5 with the "create_and_destroy" test added to "category_test.rb". The line assert_equal(initial_rec_count + 1, Category.count should have a closing parenthesis. assert_equal(initial_rec_count + 1, Category.count)
Hope it helps with any problems. Happy coding everyone ;) -
Small typo page 5
2007-08-21 09:05:04 Bill Walton | [View]
Hi,
Thanks for the catch! I've forwarded the request to fix the typo to my editor. I appreciate you making the time to let us know.
Best regards,
Bill
-
Suggestions
2007-07-24 11:59:56 DaveMatuszek [View]
Great article! I think I'm getting the hang of this!
I'm about to subject my class of ~30 unsuspecting students to the first tutorial in this series. (Maybe, if it goes fast enough, the second as well.)
Mino typos: There are missing close parens in category_test.rb test_create_and_destroy:
assert_equal(initial_rec_count + 1, Category.count
and in the same method in recipe_test.rb:
assert_equal(initial_rec_count + 1,Recipe.count
I had to work my way through the following code:
partial_name = ''
'a'.upto('y') {|letter| partial_name << letter}
rec_with_borderline_name = Category.new(:name => (partial_name * 4))
I think the following is clearer, and therefore less distracting from the point you are making:
rec_with_borderline_name = Category.new(:name => ("a" * 100))
Actually, I used
max_length = 100
rec_with_borderline_name = Category.new(:name => ("a" * max_length))
in order to avoid a "magic number." Now, if I can only figure out a way to get that number from the database itself....
Again, great job, and very helpful. Thank you! -
Suggestions
2007-08-21 09:09:06 Bill Walton | [View]
Hi Dave,
I apologize for not replying sooner. The O'Reilly feedback system sends emails when folks post, but I obviously missed yours. Thanks for making the time to write and for the feedback. Hope you'll let us know about your class and their reaction!
Best regards,
Bill










I created the new database cookbook2. Then, I edited the database.yml file. However, after this step, I can not restart the web server.
I think that there has been some sort of change in the syntax that I need to use in the database.yml file. I am using mySql Front version 4.2 and I downloaded Instant Rails. The tutorial says to edit the database.yml file to look like this:
development:
adaper: mysql
database: cookbook2
username: root
password:
host: localhost
test:
adaper: mysql
database: cookbook2
username: root
password:
host: localhost
production:
adaper: mysql
database: cookbook2
username: root
password:
host: localhost
I did that and then I try to run ruby script\server and I get an error message. c:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapers/abstract/connection_specification.rb:217:in 'establish_connection': develpment database is not configured (activeRecord::AdpaterNotSpecified)
Before I edited it, the database.yml file looked like this:
# SQLite version 3.x
# gem install sqlite3-ruby (not necessary on OS X Leopard)
development:
adapter: sqlite3
database: db/development.sqlite3
timeout: 5000
# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
adapter: sqlite3
database: db/test.sqlite3
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
timeout: 5000
What am I doing wrong? Thanks.