Unit Testing Model Validations

Hello

I’m wondering if someone can put me back on the right track?

I’ve started playing with Rails for the last 2 days while reading
through the Agile Web Dev with Rails book. I’m in Chapter 7 - Unit
Testing and Validation. The book demos work fine, but I’m now also
doing my own sample app to reinforce what I’m reading at the same
time.

I’m trying to do some Unit Tests on my User Model to check the length
of the password attribute.

Some of the assertions are failing unexpectedly and I’m now at a loss.
Can someone please help me find what I’m overlooking?

Here’s the Terminal output from running the Unit Tests:


turgs:snapshots tim$ rake test:units
Loaded suite /usr/local/lib/ruby/gems/1.9.1/gems/rake-0.9.2/lib/rake/
rake_test_loader
Started
…FF.
Finished in 0.531600 seconds.

  1. Failure:
    test_Password_must_be_long_enough(UserTest) [/users/tim/Sites/
    snapshots/test/unit/user_test.rb:31]:
    Failed assertion, no message given.

  2. Failure:
    test_Password_must_be_shorter(UserTest) [/users/tim/Sites/snapshots/
    test/unit/user_test.rb:47]:
    Failed assertion, no message given.

7 tests, 25 assertions, 2 failures, 0 errors, 0 skips

Test run options: --seed 21159
rake aborted!
Command failed with status (1): [/usr/local/bin/ruby -I"lib:test" "/
usr/loc…]

Tasks: TOP => test:units
(See full trace by running task with --trace)
turgs:snapshots tim$


Here’s the User model code:


class User < ActiveRecord::Base
has_many :accounts, :dependent => :destroy

validates :email, :presence => true
validates :name, :presence => true
validates :password, :presence => true, :length =>
{ :minimum => 8, :maximum => 2000, :message => ‘should be between 8
and 2000 characters’ }
validates :security_question, :presence => true
validates :security_answer, :presence => true
validates :mobile_phone, :length =>
{ :minimum => 8, :maximum => 30, :message => ‘should be between 8 and
30 characters’}

end


Here’s the Unit Test user_test.rb file:


require ‘test_helper’

class UserTest < ActiveSupport::TestCase

test “User attributes must not be empty” do
user = User.new
assert user.invalid?
assert user.errors[:email].any?
assert user.errors[:name].any?
assert user.errors[:password].any?
assert user.errors[:security_question].any?
assert user.errors[:security_answer].any?
assert user.errors[:mobile_phone].any?
end

test “Password must be long enough” do
user = User.new(:email => “[email protected]”,
:name => “Adam”,
:security_question => “What’s my name?”,
:security_answer => “Adam”)

user.password = "1234567"
assert user.invalid?
assert_equal "should be between 8 and 2000 characters",

user.errors[:password].join(’; ')

user.password = "12345678"
assert user.valid? # this assertion is failing

user.password = "123456789"
assert user.valid?

end

test “Password must be shorter” do
user = User.new(:email => “[email protected]”,
:name => “Eve”,
:security_question => “What’s my name?”,
:security_answer => “Eve”)

user.password = "12345678901234567890...01234567890123456789" #

this is 1999 chars long, I’ve just cut it for this email.
assert user.valid? # this assertion is failing

user.password = "12345678901234567890...012345678901234567890" #

this is 2000 chars long, I’ve just cut it for this email.
assert user.valid?

user.password = "12345678901234567890...0123456789012345678901" #

this is 2001 chars long, I’ve just cut it for this email.
assert user.invalid?
assert_equal ‘should be between 8 and 2000 characters’,
user.errors[:password].join(’; ')

end

end


Any help would be greatly appreciated.

Cheers
Tim

(comments inline)

On Aug 31, 2011, at 11:22 PM, Turgs wrote:

I’m trying to do some Unit Tests on my User Model to check the length
turgs:snapshots tim$ rake test:units

usr/loc…]

validates :security_question, :presence => true
Here’s the Unit Test user_test.rb file:
assert user.invalid?
user = User.new(:email => “[email protected]”,
assert user.valid? # this assertion is failing
This test is meant to test one thing – that a password must be at least
8 characters. Testing “user.valid?” tests much more than that. That
could be false for any number of reasons – perhaps you have a
validation that rejects “example.com” as a valid email address.

I would test that at least one error exists on the :password attribute
for the user and if you want, take it a step farther and test that the
type of error is :too_short (see 5.1.2 of

for what i’m talking about)

I would also break this up into individual tests…

test “user is invalid when password is too short”
test “user is invalid when password is too long”
test “user is valid when password is just right”

               :security_answer   => "Eve")

user.password = “12345678901234567890…01234567890123456789” #
this is 1999 chars long, I’ve just cut it for this email.

You might try this for readability.

user.password = “x” * 1999 # results in a string of x’s of length 1999.

Good luck!

Turgs wrote in post #1019574:

test “Password must be long enough” do
user = User.new(:email => “[email protected]”,
:name => “Adam”,
:security_question => “What’s my name?”,
:security_answer => “Adam”)

user.password = "1234567"
assert user.invalid?
assert_equal "should be between 8 and 2000 characters",

user.errors[:password].join(’; ')

user.password = "12345678"
assert user.valid? # this assertion is failing

Is the mobile phone number between 8 and 30 characters?
user.errors[:password] only contains the password errors–not errors for
other attributes. You would have to write something like:

user.errors.values.join(’;’)

(values comes from the key/value pairs in errors)

You can write the length validations more succinctly:

validates :password, :presence => true,
:length => 8…2000

And to form the strings to test against, I hope you are not writing them
out by hand:

user.password = ("123456790" * 200).chop
assert user.valid? # this assertion is failing

user.password = "1234567890" * 200


user.password = ("1234567890" * 200) + '1'
assert user.invalid?
#assert_equal 'should be between 8

Instead of using “user.valid?”, how can I make it more specific so I’m
just testing the password attribute?

Philip H. wrote in post #1019611:

could be false for any number of reasons – perhaps you have a
validation that rejects “example.com” as a valid email address.

Does he? Here’s his model:

class User < ActiveRecord::Base
has_many :accounts, :dependent => :destroy

validates :email, :presence => true
validates :name, :presence => true
validates :password, :presence => true, :length =>
{ :minimum => 8, :maximum => 2000, :message => ‘should be between 8
and 2000 characters’ }
validates :security_question, :presence => true
validates :security_answer, :presence => true
validates :mobile_phone, :length =>
{ :minimum => 8, :maximum => 30, :message => ‘should be between 8 and
30 characters’}

end

assert user.errors[:password].empty?

Also note that this test doesn’t do what it’s advertised to do:

test “User attributes must not be empty” do
user = User.new
assert user.invalid?
assert user.errors[:email].any?
assert user.errors[:name].any?
assert user.errors[:password].any?
assert user.errors[:security_question].any?
assert user.errors[:security_answer].any?
assert user.errors[:mobile_phone].any?
end

Because some attributes have validations other than :presence, an error
does not mean the attribute was missing.

I think you are going about your testing all wrong. Instead, you should
create users that have something wrong with them to test if the
validations catch the errors. For instance,

test “User’s name cannot be blank” do
#create user with blank name
assert user.valid?
end

It might save some typing to setup each test with a valid user, and
inside the test change one attribute to something that isn’t valid.

Read section 3 and section 8 of the railsguide on testing here:

On Sep 1, 2011, at 9:13 AM, 7stud – wrote:

Philip H. wrote in post #1019611:

could be false for any number of reasons – perhaps you have a
validation that rejects “example.com” as a valid email address.

Does he? Here’s his model:

No, he doesn’t. Sorry, I should have been more clear. The problem with
using valid? is that at some point he may add a validation that causes
the user to be invalid even though the password is okay. Now you’ve
got a test for password failing even though the password is okay and
that leads to madness :slight_smile:

7stud – wrote in post #1019720:

Also note that this test doesn’t do what it’s advertised to do:

test “User attributes must not be empty” do
user = User.new
assert user.invalid?
assert user.errors[:email].any?
assert user.errors[:name].any?
assert user.errors[:password].any?
assert user.errors[:security_question].any?
assert user.errors[:security_answer].any?
assert user.errors[:mobile_phone].any?
end

Because some attributes have validations other than :presence, an error
does not mean the attribute was missing.

Actually, it’s worse than that. Suppose all the attributes were empty.
That test would pass, and therefore one would think from the title of
the test, that the User’s attributes were all present.

I think you are going about your testing all wrong. Instead, you should
create users that have something wrong with them to test if the
validations catch the errors. For instance,

test “User’s name cannot be blank” do
#create user with blank name
assert user.valid?
end

It might save some typing to setup each test with a valid user, and
inside the test change one attribute to something that isn’t valid.

Read section 3 and section 8 of the railsguide on testing here:

Testing Rails Applications — Ruby on Rails Guides