Test driven development with rails

As a real fan of test driven development (TDD) I was interested in finding out how rails takes care of testing within its applications. In this section, I will be writing test to cover validations.
Still working with my blackbook demo app, I wanted to test that user cannot save record without supplying first_name, last_name and profile_name.

Testing for presence

1
2
3
4
5
6
7
8
9
10
11
12
require 'test_helper'
 
class UserTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
  test "a user should enter a first name" do
  	user = User.new
  	assert !user.save
  	assert !user.errors[:first_name].empty?
  end
end

In above I opened up my user model test file in test/unit/user_test.rb every test in ruby begin with the word test as in line 7, then followed with a string of what the test does. In line 8 I instantiated the user class, important to note the capital case on the name, User this indicates class name.
In line 9 I assert that a save is not successful, this is assuming that no data has been submitted for a save and so I expect it to fail. Line 10 I assert that the error object contains an error on the users firstname.

1
   ruby -Itest test/unit/user_test.rb

I then ran the code by typing the above into the terminal, this fails as expected, so I went into the model for user and adding a validation for presence

1
   validates :first_name, presence: true

The validates method above accepts as parameters :first_name symbol and make sure presence: is true.

1
2
3
4
5
6
7
8
9
10
11
   test "a user should enter a last name" do
        user = User.new
  	assert !user.save
  	assert !user.errors[:last_name].empty?
  end
 
  test "a user should enter a profile name" do
  	user = User.new
  	assert !user.save
  	assert !user.errors[:profile_name].empty?
  end

I then repeated the same for last_name and profile_name.

1
2
validates :last_name, presence: true
validates :profile_name, presence: true

I then updated the user model file to also include validation requirements for both last_name and profile_name.

Creating some fixture data

1
2
3
4
5
kingsley:
   first_name: "kingsley"
   last_name: "ijomah"
   profile_name: "kingsleyijomah"
   email: "kingsley@example.com"

Next I wanted to test that username is always unique, for this I had to create a users fixture file, fixtures simulate database records and allows rails code to be tested out. I created user fixture in test/fixtures/users.yml> also good to note that using tabs in yml causes errors and so I used space bar instated (http://www.yaml.org/faq.html), in this fixture file I added kingsley profile record as in the code above.

Testing for uniqueness

1
2
3
4
5
6
7
test "a user has unique profile name" do
  user = User.new
  user.profile_name = users(:kingsley).profile_name
  assert !user.save
  #puts user.errors.inspect
  assert !user.errors[:profile_name].empty?
end

Now that I have a fixture file for user, I then wrote a test before I wrote the actual code. Line 3 sets the user model object’s profile_name to the profile name that already exists in the fixtures database using users(:kingsley).profile_name “users” loads the users fixture. I now try to save this profile_name the second time and assert that it fails on line 4 and expect that the error for profile_name is not empty in line 6.

1
validates :profile_name, presence: true, uniqueness: true

Running the test failed as expected so I implemented a solution by adding uniqueness: true to the user model validation and ran the test again so everything passed.

Testing for a formats

I wanted to test that a users profile_name never contains spaces.

1
2
3
4
5
6
7
test "a user should have a profile name without spaces" do
  user = User.new
  user.profile_name = "kingsley ijomah"
  assert !user.save
  assert !user.errors[:profile_name].empty?
  assert user.errors[:profile_name].include?("Must be formated correctly.")
  end

So line 3 I added a profile_name to user with space between first and lastname, I then assert that this does not save successfully on line 4 I also assert that there is an error message present in the error objects profile_name on line 6 I added .include?("Must be formated correctly.") method, this says that the error message I expect will be Must be formated correctly.

1
2
3
4
5
6
validates :profile_name, presence: true,
                            uniqueness: true,
                            format: {
                            	with: /a-zA-Z0-9_-/,
                            	message: 'Must be formated correctly.'
                            }

In the solution implementation, I added format hash code to profile_name validates method, with says that we only accept lowercase a-z and upercase A-Z with underscores and dashes, anything not included in the list is banned, just like a white space.
The supplied message is then what is sent to our include method of the test, this message is also displayed to the view files.

Complete code files

User test file:

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
require 'test_helper'
 
class UserTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
  test "a user should enter a first name" do
  	user = User.new
  	assert !user.save
  	assert !user.errors[:first_name].empty?
  end
 
  test "a user should enter a last name" do
  	user = User.new
  	assert !user.save
  	assert !user.errors[:last_name].empty?
  end
 
  test "a user should enter a profile name" do
  	user = User.new
  	assert !user.save
  	assert !user.errors[:profile_name].empty?
  end
 
  test "a user has unique profile name" do
  	user = User.new
  	user.profile_name = users(:kingsley).profile_name
  	assert !user.save
  	#puts user.errors.inspect
  	assert !user.errors[:profile_name].empty?
  end
 
  test "a user should have a profile name without spaces" do
  	user = User.new
  	user.profile_name = "kingsley ijomah"
  	assert !user.save
  	assert !user.errors[:profile_name].empty?
  	assert user.errors[:profile_name].include?("Must be formated correctly.")
  end
end

Complete code files

User model file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
   attr_accessible :password, :password_confirmation, :email, :first_name, :last_name, :profile_name
 
   has_many :statuses
 
   validates :first_name, presence: true
   validates :last_name, presence: true
   validates :profile_name, presence: true,
                            uniqueness: true,
                            format: {
                            	with: /a-zA-Z0-9_-/,
                            	message: 'Must be formated correctly.'
                            }
 
   def full_name
   	   first_name + " " + last_name
   end
end

Tags: ,