12: Refactoring User Name Part 3
(view original Railscast)
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 endp(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.