Posts Tagged ‘unit testing’

Chores: a test driven website – Part 7 (return of the son of Chores)

Saturday, January 24th, 2009

This is part of the Chores series of posts

When last we worked on Chores, we had just generated two Models; Identity and Child. The idea behind these is that Child will serve as a many-to-many link between identities via has_many :through. First let’s look over the generated migrations and make a couple changes.

1
2
3
4
5
6
7
8
9
10
11
12
13
class CreateIdentities < ActiveRecord::Migration
   def self.up
     create_table :identities do |t|
       t.string :identifier, :null => false, :limit => 100
 
       t.timestamps
    end
  end
 
  def self.down
    drop_table :identities
  end
end

and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CreateChildren < ActiveRecord::Migration
   def self.up
     create_table :children do |t|
       t.integer :parent_id, :null => false
       t.integer :child_id, :null => false
 
       t.timestamps
    end
  end
 
  def self.down
    drop_table :children
  end
end

and let’s migrate.

$ rake db:migrate (in c:/rails/chores)
==  CreateIdentities: migrating ===============================================
 -- create_table(:identities)    -> 0.0630s
==  CreateIdentities: migrated(0.0630s) ======================================
==  CreateChildren: migrating =================================================
 -- create_table(:children)    -> 0.0160s
==  CreateChildren: migrated (0.0160s) ========================================

and update the Blueprints (./test/blueprints.rb).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'machinist'  # if you installed the gem
 
Chore.blueprint do
  description "Put the dirty clothes in the hamper"
end
 
Identity.blueprint do
  identifier "#{Faker::Internet.user_name}.#{Faker::Internet.domain_name}"
end
 
Child.blueprint do
  child { Identity.make }
  parent { Identity.make }
end

And remember to add the reference to the Faker gem to ./test/test_helper

require 'faker'

I added not null constraints to the three columns in the two tables I created so I’m going to make note of that in the test files and start by making a child test (./test/unit/child_test.rb).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
require 'test_helper'
 
class ChildShouldBeValidated < ActiveSupport::TestCase
 
  specify "that a parent is required" do
    child = Child.new :identity => Identity.make
    invalid child
    child.parent = Identity.make
    valid child
  end
 
  specify "that a child is required" do
    child = Child.new :parent => Identity.make
    invalid child
    child.identity = Identity.make
    valid child
  end
 
  specify "message for a null identity" do
    child = Child.new :parent => Identity.make
    validation_message_for child, :identity, "can not be blank."
  end
 
  specify "message for a null parent" do
    child = Child.new :identity => Identity.make
    validation_message_for child, :parent, "can not be blank."
  end
 
  protected
    def invalid model
      assert !model.valid?
    end
 
    def valid model
      assert model.valid?
    end
 
    def validation_message_for model, column, message
      invalid model
      assert_equal message, model.errors.on(column)
    end
end

that fails spectacularly..

$ rake test:units (in c:/rails/chores)
c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/ rake_test_loader.rb"
"test/unit/child_test.rb" "test/unit/chore_test.rb" "test/u nit/identity_test.rb"
 
Loaded suite c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
 
Started
EEEE......
Finished in 0.766 seconds.
  1) Error: test_message_for_a_null_identity(ChildShouldBeValidated):
  ActiveRecord::UnknownAttributeError: unknown attribute: parent
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2587:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'each'
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2283:in 'initialize'
    ./test/unit/child_test.rb:20:in 'new'
    ./test/unit/child_test.rb:20:in 'test_message_for_a_null_identity'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in '__send__'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in 'run'
 
  2) Error: test_message_for_a_null_parent(ChildShouldBeValidated):
  ActiveRecord::UnknownAttributeError: unknown attribute: identity
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2587:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'each'
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2283:in 'initialize'
    ./test/unit/child_test.rb:25:in 'new'
    ./test/unit/child_test.rb:25:in 'test_message_for_a_null_parent'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in '__send__'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in 'run'
 
  3) Error: test_that_a_child_is_required(ChildShouldBeValidated):
  ActiveRecord::UnknownAttributeError: unknown attribute: parent
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2587:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'each'
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2283:in 'initialize'
    ./test/unit/child_test.rb:13:in 'new'
    ./test/unit/child_test.rb:13:in 'test_that_a_child_is_required'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in '__send__'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in 'run'
 
  4) Error: test_that_a_parent_is_required(ChildShouldBeValidated):
  ActiveRecord::UnknownAttributeError: unknown attribute: identity
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2587:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'each'
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2583:in 'attributes='
    c:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2283:in 'initialize'
    ./test/unit/child_test.rb:6:in 'new'
    ./test/unit/child_test.rb:6:in 'test_that_a_parent_is_required'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in '__send__'
    c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in 'run'
 
10 tests, 9 assertions, 0 failures, 4 errors
rake aborted!
Command failed with status (1): [c:/Ruby/bin/ruby -Ilib;test c:/Ruby/lib/r...]
 
See full trace by running task with --trace)

Looks like I need to set up some attributes. Let’s try the following class definition

1
2
3
4
5
6
7
class Child < ActiveRecord::Base
  belongs_to :identity
  belongs_to :parent,               :class_name => 'Identity'
 
  validates_presence_of :identity,  :message => 'can not be blank.'
  validates_presence_of :parent,    :message => 'can not be blank.'
end

That gives us green

$ rake test:units (in c:/rails/chores)
c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/ rake_test_loader.rb" "test/unit/child_test.rb" "test/unit/chore_test.rb" "test/u nit/identity_test.rb" Loaded suite c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
 
Started
..........
Finished in 0.781 seconds
10 tests, 17 assertions, 0 failures, 0 errors

But wait!

If you have been following along in this series of posts, you might have noticed that I copied, pasted and then modified the code from the last test. Copy and Pasting is bad and the opposite of DRY right? Well, yeah. That’s right. And it’s also why it’s time to refactor. Here are the results of the refactoring.

./test/test_helper.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require File.expand_path(File.dirname(__FILE__) + "/blueprints")
require 'test_help'
require 'machinist' #if you installed as gem rather than plugin
require 'faker'
 
class ActiveSupport::TestCase
  self.use_transactional_fixtures = true
  self.use_instantiated_fixtures  = false
 
  def invalid model
    assert !model.valid?
  end
 
  def valid model
    assert model.valid?
  end
 
  def validation_message_for model, column, message
    invalid model
    assert_equal message, model.errors.on(column)
  end
end
 
def specify *args, &block
  test(*args, &block)
end

./test/unit/child_test.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
require 'test_helper'
 
class ChildShouldBeValidated < ActiveSupport::TestCase
 
  specify "that a parent is required" do
    child = Child.new :identity => Identity.make
    invalid child
    child.parent = Identity.make
    valid child
  end
 
  specify "that a child is required" do
    child = Child.new :parent => Identity.make
    invalid child
    child.identity = Identity.make
    valid child
  end
 
  specify "message for a null identity" do
    child = Child.new :parent => Identity.make
    validation_message_for child, :identity, "can not be blank."
  end
 
  specify "message for a null parent" do
    child = Child.new :identity => Identity.make
    validation_message_for child, :parent, "can not be blank."
  end
end

./test/unit/chore_test.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
require 'test_helper'
class ChoreShouldBeValidated < ActiveSupport::TestCase
 
  specify "that a description is required" do
    chore = Chore.new
    invalid chore
    chore.description = "Clean your room"
    valid chore
  end
 
  specify "that a description of 255 characters is valid" do
    chore = Chore.new :description => two_hundred_fifty_character_string
    valid chore
  end
 
  specify "that a description of 256 characters is invalid" do
    chore = Chore.new :description => two_hundred_fifty_one_character_string
    invalid chore
  end
 
  specify "message for a null description" do
    chore = Chore.new
    validation_message_for chore, :description, "Description cannot be blank."
  end
 
  specify "message for an overly long description" do
    chore = Chore.new :description => two_hundred_fifty_one_character_string
    validation_message_for chore, :description, "Description cannot be longer than 255 characters."
  end
 
  protected
    def two_hundred_fifty_character_string
      "This is a really long string that is the description of a chore and it is used to validate that the chore model will accept a string of 255 characters.  This string is longer than the longest string ever which also included some numbers 012345678901234567"
    end
 
    def two_hundred_fifty_one_character_string
      two_hundred_fifty_character_string + "1"
    end
end

As you can see, I’ve moved the duplicates to test_helper.rb, which clears up the duplicate code. Another option that you can explore to reduce duplication is mixins. That just about wraps up this post, next time I will work on testing the identity model.

Chores: a test driven website, part 4 (judgement day)

Wednesday, January 14th, 2009

Update This is part of the Chores series of posts

Back to building Chores, which started back here at the beginning. After you have a test running, it’s time for refactoring. Refactoring being the process of improving code without changing it’s functionality. You can do this in a variety of ways, commonly you will want to do this to keep it DRY (remove duplication) and improve readability. Once you have refactored your application or your tests, you will want to run the tests to make sure that they are all still passing.

This is one of the great benefits of automated testing. You can improve your code systematically and still be (for me, reasonably, since I’m still improving my testing skills and gaining confidence) sure that everything is still functioning as it was.

After a look through our code I didn’t see anything to change in the model and have modifed the unit test to read as follows.

require 'test_helper'
 
class ChoreShouldBeValidated < ActiveSupport::TestCase
 
  specify "that a description is required" do
    chore = Chore.new
    invalid chore
    chore.description = "Clean your room"
    valid chore
  end
 
  specify "that a description of 255 characters is valid" do
    chore = Chore.new :description => max_description
    valid chore
  end
 
  specify "that a description of 256 characters is invalid" do
    chore = Chore.new :description => max_description + "8"
    invalid chore
  end
 
  specify "the validation message for description" do
    chore = Chore.new
    invalid chore
    assert_equal "Description cannot be blank and must be less than 255 characters long.", chore.errors.on(:description)
  end
 
  protected
    def max_description
      "This is a really long string that is the description of a chore and it is used to validate that the chore model will accept a string of 255 characters.  This string is longer than the longest string ever which also included some numbers 012345678901234567"
    end
 
    def invalid model
      assert !model.valid?
    end
 
    def valid model
      assert model.valid?
    end
end

You’ll notice the spec style specify rather than test block. This can be achieved by simply adding the following to your test_helper file after the Test::Unit::TestCase class.

def specify *args, &block
  test(*args, &block)
end

I could move the valid and invalid methods to the test helper at this point as well, but so far there is no reason to so I am leaving it as it is, for now.

At this point I’m pretty happy with how it looks and reads. It’s similar to a BDD spec, but is still using the standard Test::Unit. However, looking back at this, I’m still not happy with the not null validation and the length validation being combined so I’m going to separate them out.

After making the changes and some refactors, my Chore model and matching test look like this:

1
2
3
4
5
6
7
8
9
class Chore < ActiveRecord::Base
  validates_length_of   :description,
                        :maximum => 255,
                        :allow_nil => true,
                        :message => "Description cannot be longer than 255 characters."
 
  validates_presence_of :description,
                        :message => "Description cannot be blank."
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
require 'test_helper'
 
class ChoreShouldBeValidated < ActiveSupport::TestCase
 
  specify "that a description is required" do
    chore = Chore.new
    invalid chore
    chore.description = "Clean your room"
    valid chore
  end
 
  specify "that a description of 255 characters is valid" do
    chore = Chore.new :description => max_description
    valid chore
  end
 
  specify "that a description of 256 characters is invalid" do
    chore = Chore.new :description => overly_long_description
    invalid chore
  end
 
  specify "message for a null description" do
    chore = Chore.new
    validation_message_for chore, :description, "Description cannot be blank."
  end
 
  specify "message for an overly long description" do
    chore = Chore.new :description => overly_long_description
    validation_message_for chore, :description, "Description cannot be longer than 255 characters."
  end
 
  protected
    def max_description
      "This is a really long string that is the description of a chore and it is used to validate that the chore model will accept a string of 255 characters.  This string is longer than the longest string ever which also included some numbers 012345678901234567"
    end
 
    def overly_long_description
      max_description + "8"
    end
 
    def invalid model
      assert !model.valid?
    end
 
    def valid model
      assert model.valid?
    end
 
    def validation_message_for model, column, message
      invalid model
      assert_equal message, model.errors.on(column)
    end
end

Chores: A test driven website: part 3 (the revenge)

Friday, January 9th, 2009

Update This is part of the Chores series of posts

It’s friday; the floors are piling up with dirty clothes, old homework and beds are unmade. We need Chores, a Ruby on Rails website written with test driven principles in mind and created by the author of this article.

This post is a continuation of parts 1 and 2 in which I am documenting the steps with which I am creating a rails site and writing matching tests with Cucumber and Test::Unit. When we left off last time I had found that I need a Chore model to continue. At a minimum, we are going to need a description for the chore. So let’s get to it:

1
2
3
4
5
6
7
8
9
$ ruby script/generate model Chore description:string
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/chore.rb
      create  test/unit/chore_test.rb
      create  test/fixtures/chores.yml
      create  db/migrate
      create  db/migrate/20090109221900_create_chores.rb

Now I have a decision to make, I know that I want to set the :limit and :null attributes in the migration file. I can make the changes now and then write the tests on the matching validation or I can run the migration now and then write the tests on the validation and modify the columns in the db if necessary. Because I am lazy and because I want to be sure the db constraints are in place I’m going to opt for the first option and then run my migration.

1
2
3
4
5
6
7
8
9
10
11
12
13
class CreateChores < ActiveRecord::Migration
  def self.up
    create_table :chores do |t|
      t.string :description, :null => false, :limit => 255
 
      t.timestamps
    end
  end
 
  def self.down
    drop_table :chores
  end
end

and

$ rake db:migrate
==  CreateChores: migrating ===================================================
-- create_table(:chores)
   -> 0.0160s
==  CreateChores: migrated (0.0160s) ==========================================

Assert yourself!

At the core of unit testing is the assertion. An assertion is a statement that something will always be true. In fact, if an assertion evaluates to false, the test will fail. Test::Unit provides a wide variety of assertions ranging from a vanilla assert to the one that I find myself using most frequently, assert_equal. There are also many more available for use in specific situations.

Test!

Now let’s write some tests to ensure our model validates the data. First I’m going to make sure that the description is required and second I’m going to make sure that the description can be a maximum of 255 characters. So lets open up the file ./test/unit/chore_test.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'test_helper'
 
class ChoreTest < ActiveSupport::TestCase
  test "description is required" do
    chore = Chore.new
    assert !chore.valid?
    chore.description = "Clean your room"
    assert chore.valid?
  end
 
  test "a description of 255 characters is valid" do
    chore = Chore.new :description => "This is a really long string that is the description of a chore and it is used to validate that the chore model will accept a string of 255 characters.  This string is longer than the longest string ever which also included some numbers 012345678901234567"
    assert chore.valid?
  end
 
  test "a description of 256 characters is invalid" do
    chore = Chore.new :description => "This is a really long string that is the description of a chore and it is used to validate that the chore model will accept a string of 255 characters.  This string is longer than the longest string ever which also included some numbers 0123456789012345678"
    assert !chore.valid?
  end
end

As you can see I have written 3 simple tests. The first verifies that the description cannot be null, the second proves that a 255 character string is valid, and the third shows that a 256 character string is not valid. Lets run these tests and see what we get.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ rake test:units
(in c:/rails/chores)
c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/chore_test.rb"
Loaded suite c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
 
Started
.FF
Finished in 0.75 seconds.
 
  1) Failure:
test_a_description_of_256_characters_is_invalid(ChoreTest)
    [./test/unit/chore_test.rb:18:in `test_a_description_of_256_characters_is_invalid'
     c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `__send__'
     c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']:
<false> is not true.
 
  2) Failure:
test_description_is_required(ChoreTest)
    [./test/unit/chore_test.rb:6:in `test_description_is_required'
     c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `__send__'
     c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']:
<false> is not true.
 
3 tests, 3 assertions, 2 failures, 0 errors
rake aborted!
Command failed with status (1): [c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/r...]
 
(See full trace by running task with --trace)

What does this mean? The summary tells us what happened in a nutshell. At the bottom it says “3 tests”, that means that it ran 3 tests. That is good because we wrote 3 tests. Next, it says “3 assertions”, that means that in running the 3 tests, the testing framework encountered 3 assert statements. But we wrote 4 assertions! Why are there only 3 listed? This is because, within a test, when the framework encounters a failed assertion, it stops running that test. So we must have a test with an assertion that was not executed because another assertion failed before it was executed.

Moving on through the summary, it says “2 failures”. This means that two of the things that we asserted to be true, were in fact false. If we look in the details up above we see that the test to see if 255 characters was valid has passed, this makes sense since we don’t have any validation to limit the length at this point. Finally it says “0 errors”, in this context you can think of errors as exceptions. Basically it means that the code that ran, ran without throwing an exception.

Now let’s make these tests pass. The first failing test shows that the length of the description is not being limited to 255 characters. So let’s do that.

1
2
3
class Chore < ActiveRecord::Base
  validates_length_of :description, :maximum => 255
end

When we run our test we get:

$ rake test:units
(in c:/rails/chores)
c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/chore_test.rb"
Loaded suite c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
 
Started
...
Finished in 1.516 seconds.
 
3 tests, 4 assertions, 0 failures, 0 errors

Huh?

But we never checked to see if the description was null! Why are all of our tests passing? If we look at the api docs for validates_length_of we will see that it does not allow nil by default, and that to allow nil you have to pass the symbol :allow_nil. Well this is an interesting development, let’s make sure that the validation message is going to make sense by adding a test.

  test "validation message for description" do
    chore = Chore.new
    chore.valid?
    assert_equal "Description cannot be blank and must be less than 255 characters long.", chore.errors.on(:description)
  end
$ rake test:units
(in c:/rails/chores)
c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/chore_test.rb"
Loaded suite c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
 
Started
...F
Finished in 0.781 seconds.
 
  1) Failure:
test_validation_message_for_description(ChoreTest)
    [./test/unit/chore_test.rb:24:in `test_validation_message_for_description'
     c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `__send__'
     c:/Ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']:
<"Description cannot be blank and must be less than 255 characters long."> expected but was
<"is too long (maximum is 255 characters)">.
 
4 tests, 5 assertions, 1 failures, 0 errors
rake aborted!
Command failed with status (1): [c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/r...]
 
(See full trace by running task with --trace)

Looks like it’s a good thing that we checked the error message. The current message, “is too long (maximum is 255 characters)”, would not have made any sense if the user had left the description nil. Let’s update our model with the new message

1
2
3
class Chore < ActiveRecord::Base
  validates_length_of :description, :maximum => 255, :message => "Description cannot be blank and must be less than 255 characters long."
end
$ rake test:units
(in c:/rails/chores)
c:/Ruby/bin/ruby -Ilib;test "c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/chore_test.rb"
Loaded suite c:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
 
Started
....
Finished in 0.797 seconds.
 
4 tests, 5 assertions, 0 failures, 0 errors

Our unit tests are passing. Let’s commit and then take a step back to see where we are.

$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified:   config/routes.rb
#       modified:   db/development.sqlite3
#       modified:   db/test.sqlite3
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       app/controllers/chores_controller.rb
#       app/controllers/home_controller.rb
#       app/helpers/chores_helper.rb
#       app/helpers/home_helper.rb
#       app/models/
#       app/views/
#       db/migrate/
#       db/schema.rb
#       test/fixtures/
#       test/functional/
#       test/unit/
no changes added to commit (use "git add" and/or "git commit -a")
 
$ git add .
$ git commit -am "Added the Chore model"
$ git push origin

Well the easy way to see where we are is to re-run our feature with cucumber

$ cucumber features
Story:  Define chores  # features/define_chores.feature
As a parent
I want to define chores
So that I can assign them to my children
  Scenario: Creating a chore           # features/define_chores.feature:6
    Given I am on the homepage      # features/step_definitions/chores_steps.rb:1
    When I follow "Add Chore"         # features/step_definitions/webrat_steps.rb:8
      Called id for nil, which would mistakenly be 4 -- if you really wanted the
 id of nil, use object_id (ActionView::TemplateError)
      On line #1 of app/views/chores/new.html.erb
 
      1: <% form_for(@chore) do |f| %>
      2:   <%= f.error_messages %>
      3:
      4:   <p>

So chore is nil in the view, but we have a model for it now. The next step is to look back at what we’ve written and see if there are any opportunities for refactoring. Then we will make sure that our controller is setting the @chore instance variable for us. If you are interested in reading more about testing in Rails, here are some links for further reading: