views:

22

answers:

2

I've been struggling to understand why I can easily use seeds.rb to create new Users and their default associations, but when running individual a unit test causes errors. I've tried to get around calling 'Role' as it causes errors in the unit test. I am relatively new to unit testing, but have been using Rails for several years already. Although it all works, I want the tests to come out error free.

ruby 1.8.7
gem 1.3.7
Rails 2.3.8

app/models/user.rb

class User < ActiveRecord::Base

  has_many :user_roles, :dependent => :destroy              
  has_many :roles, :through => :user_roles, :uniq => true, :order => :id

  has_one  :contact, :as => :owner, :dependent => :destroy

  attr_accessor   :password

  before_save :build_default_associations, :if => :new_record?

def build_default_associations
  update_encrypted_password
  build_contact(:email => name) 
  user_roles.build(:role_id => Role.find_by_name("subscriber")id )
  #
  # the below also works in seeds.rb but causes a different error
  # roles << Role.find_by_name("subscriber") 
  # ActiveRecord::AssociationTypeMismatch: Role(#2162845660) expected, got NilClass(#2148404100)
  # 
end

db/seeds.rb

# This places the default Role in the db
Role.create({:name => "subscriber", :description => "This will be the default"})
# associations initialize and save with 
User.create(:name => "[email protected]", :password => "abcde")  

test/unit/user_test.rb

# assertions DO NOT fail, but raise the error below! 
def test_should_create_user_and_add_default_role
  user = User.create(:name => '[email protected]', :password => 'abcde') #minimum length password
  assert !user.new_record?
  assert user.roles.exists?("subscriber")
end

ruby test/unit/user_test.rb

1) Error:
test_should_create_user_and_add_default_role(UserTest):
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
app/models/user.rb:31:in 'build_default_associations'
unit/user_test.rb:31:in `test_should_create_user_and_add_default_role'

Any thoughts? I can't seem to find any best practices that prohibit this...

A: 

What does Role.default look like? It seems like it's returning nil in the test environment, so I'd assume you're missing the "default" Role record in the test DB.

By default each test is run within a transaction and then rolled back once it's finished, so you'll need to explicitly setup any DB state your tests require. You could use perhaps use fixtures or create it in a setup block.

Paul Horsfall
I've edited the above to replace Role.default with Role.find_by_name("subscriber"), since it was confusing the other way.If I try roles.build(:name => "subscriber") I get Role already taken, because the Roles are created by seeds.rb as they are shared by Users through UserRoles.I also included the portion of seeds.rb that load the default Role in the the db. Should have also noted is that I have expanded the db:test rake task in lib/tasks/test_with_seeds.task, so that seeds.rb is used to populate the Roles table in the database. Can I not reference another Model directly within a Model?
domino
A: 

I needed to prevent the default fixtures from loading and destroying my test data, which is loading from db/seeds.rb by editing test/test_helper.rb

Still it was not clear from the errors that my test data was munged, but once I wrote the tests that verified my default Roles were (not) in place....

test/unit/user_test.rb

def test_users_exist
  assert_equal User.count, 8   # test was expecting 2, b/c of default yml fixtures!
end

test/test_helper.rb

# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
# fixtures :all  <--- for now load none, since testing data from db/seeds.rb

test/unit/user_test.rb

# No error, No fail 
def test_should_create_user_and_find_default_role
  user = User.create(:name => '[email protected]', :password => 'abcde')
  assert user.roles.exists?(:name => "subscriber") 
end
domino