Rails: Unable to initialize has_many :through association from within unit test using model class name
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 => "valid@email.com", :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 => 'ok@good.org', :password => 'abcde') #minimum length password
assert !user.new_record?
assert user.roles.exists?("subsc开发者_如何学运维riber")
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...
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.
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 => 'ok@good.org', :password => 'abcde')
assert user.roles.exists?(:name => "subscriber")
end
精彩评论