Re: Strange Little Problem

Rob Biedenharn
Wed, 26 Sep 2007 12:47:45 -0000

On Sep 25, 2007, at 10:55 PM, qb wrote:
> I just created a simple model and started to write unit tests for it
> (I''ve cleaned out extraneous stuff).
>
> The migration is:
>
> class CreateMembers < ActiveRecord::Migration
>   def self.up
>     create_table :members do |t|
>       t.column :name, :string
>       t.column :number, :string
>     end
>   end
>
>   def self.down
>     drop_table :members
>   end
> end
>
> The model is:
>
> class Member < ActiveRecord::Base
>   validates_uniqueness_of :number
> end
>
> The testing fixture is:
>
> numeric_only_member:
>   id: 1
>   name: John Smith
>   number: 15171914

You might need:
   number: "15171914"
So that YAML keeps this a string and not an integer.  You''re not
using members(:numeric_only_member).number so it might not matter
(the database should be a string regardless).

> alpha_numeric_member:
>   id: 2
>   name: Joanne Jones
>   number: wke1234
>
> So if I write a little test case (yes I know hard coded values are
> fragile):
>
> require File.dirname(__FILE__) + ''/../test_helper''
> require ''member''

You shouldn''t need this as Rails will auto-load it when Member is
first used.

>
> class ExampleTest < Test::Unit::TestCase
>   fixtures :members
>
>   def test_uniqueness_of_member
>     dup_member = Member.new(:name => ''duplicate'',
:number =>
> ''15171914'')
>
>     assert !dup_member.save
>     assert_equal 1, dup_member.errors.count
>     assert_equal "has already been taken",
> dup_member.errors.on(:number)
>   end
> end
>
> Problem:
>
> "assert_equal 1, dup_member.errors.count" will fail with:
> test_uniqueness_of_member(SimpleTest) [test/unit/simple_test.rb:11]:
> <1> expected but was <2>.
>
> The following line would also fail with:
> test_uniqueness_of_member(SimpleTest) [test/unit/simple_test.rb:12]:
> <"has already been taken"> expected but was
> <["has already been taken", "has already been
taken"]>.
>
> Solution:
>
> If I remove " require ''member'' " from the test
case then everything
> passes. I have no idea why. (Yes I''m a newbie, please
don''t laugh and
> point). I''m digging through ActiveRecord, UnitTest, and Ruby docs
and
> source but haven''t found an answer yet. Any ideas?
>
> Thanks...qb

I *think* that Member has already been defined when your ''require
"member"'' is executed and since the idempotency of
''require'' is based
on the argument (i.e., require ''foo'' and require
''./foo'' will load
foo.rb from the current directory twice), the explicit require re-
opens the Member class and hits the validates_uniqueness_of a second
time.  In addition to removing your "require
''member''", I''d also
suggest writing your test like this:

class ExampleTest < Test::Unit::TestCase
   fixtures :members

   def test_uniqueness_of_member
     dup_member = Member.new(:name => ''duplicate'',
                             :number => members
(:numeric_only_member).number)

     assert ! dup_member.valid?
     assert dup_member.errors[:number]
     assert_match /has already been taken/, dup_member.errors.on
(:number)
   end
end

You want to know that the record isn''t valid, there''s no need
to
actually try to save it.
You should check that the field has an error message, rather than the
total error count.
I tend to test for things like error message text with regexps that
will allow some flexibility in the actual value.  (e.g., "has already
been taken. Please choose another and try again.")

However, all you''re doing is testing that the validates_uniqueness_of
works.  Don''t you think that the Rails team already does that?  You
could make a case for verifying that something like a custom format
is behaving correctly or that you''re using the :scope option as you
expect, but unless you''re just practicing and convincing yourself how
things work, I''d not normally test the things that Rails does.

-Rob

Rob Biedenharn		http://agileconsultingllc.com
Rob-xa9cJyRlE0mWcWVYNo9pwxS2lgjeYSpx@public.gmane.org