homeASCIIcasts

12: Refactoring User Name Part 3 

(view original Railscast)

Other translations: It Es Fr

The last two episodes have concentrated on refactoring and testing. By the end of the last episode we’d refactored our model, but the tests for it were in a bit of a mess. Let’s see what we can do to tidy them up.

  require 'test_helper'
  class UserTest < ActiveSupport::TestCase
    test "full name without middle initial" do
      user = User.new(:first_name => "John", :last_name => "Smith")
      assert_equal 'John Smith', user.full_name
    end
    test "full name with middle initial" do
      user = User.new(:first_name => "Paul", :middle_initial => "P", :last_name => "Hughes")
      assert_equal 'Paul P. Hughes', user.full_name
    end
    test "full name with empty middle initial" do
      user = User.new(:first_name => "John", :middle_initial => "", :last_name => "Jones")
      assert_equal 'John Jones', user.full_name
    end
  end

The tests for the User class.

We have three tests and there is a lot of duplication across them. For each test we create a new User and compare it to a string value. To remove the duplication we’ll create a method that creates a new User and returns its full_name.

def full_name(first, middle, last)
  User.new(:first_name => first, :middle_initial => middle, :last_name => last).full_name
end

The new non-test method for the UserTest class.

Now, each of our tests can be simplified so that they look like this:

test "full name without middle initial" do
  assert_equal "John Smith", full_name('John', nil, 'Smith')
end
test "full name with middle initial" do
  assert_equal 'Paul P. Hughes', full_name('Paul', 'P', 'Hughes')
end  
test "full name with empty middle initial" do
  assert_equal "John Jones", full_name('John', '', 'Jones')
end

The simplified test for a User with a middle initial.

Of course, the proof that our refactoring has worked is that the tests still all pass.

Laa-Laa:ep11 eifion$ autotest
loading autotest/rails
/opt/local/bin/ruby -I.:lib:test -rtest/unit -e "%w[test/unit/user_test.rb test/functional/users_controller_test.rb].each { |f| require f }" | unit_diff -u
Loaded suite -e
Started
...
Finished in 0.282538 seconds.
3 tests, 3 assertions, 0 failures, 0 errors

The refactored unit tests still pass.

Now that our tests are straightforward they can be moved in to a single test with three assertions. The only problem with doing that is that if one of the assertions in a test fails it is difficult to know which one it was. We can add a message to each assertion so that it identifies itself if it fails. Our final test class looks like this:

require 'test_helper'
class UserTest < ActiveSupport::TestCase
  test "full name" do
    assert_equal "John Smith", full_name('John', nil, 'Smith'), 'nil middle initial'
    assert_equal 'Paul P. Hughes', full_name('Paul', 'P', 'Hughes'), 'P middle initial'
    assert_equal "John Jones", full_name('John', '', 'Jones'), 'blank middle initial'
  end
  def full_name(first, middle, last)
    User.new(:first_name => first, :middle_initial => middle, :last_name => last).full_name
  end
end
p(title). The final refactored UserTest class.

Over the last three episodes we’ve created unit tests and refactored them along with the code they test to leave both in a much more readable and managable state. While this is a relatively simple example it should persuade you of the benefits of testing and refactoring your Ruby and Rails code.