Reputation: 169
Trying to wrap my head around rspec and proper testing and having some hard time to do the following properly
Let's say we have three classes like
Class User
belongs_to :company
has_and_belongs_to_many :roles
end
Class Company
has_many :users
end
Class Role
has_and_belongs_to_many :users
end
In the User class I have before_create callback that assign the user default 'company_admin' role, if the user is first one to be associated with the company
def check_for_initial_company_admin_role
if self.company.users.count == 0
self.roles << Role.find_by_name("company_admin")
end
end
How do I properly test in my model spec that the user gets assigned the 'company_admin' role in case he's the first user associated with the company?
UPDATE working solution
describe "#check_for_initial_company_admin_role" do
it "sets the first user created to be the administrator" do
Factory(:role)
user = Factory(:user)
user.roles.count.should be > 0
user.roles.should include Role.find_by_name("company_admin")
end
end
Factory.define :user do |f|
f.email { Factory.next(:email) }
f.password "secret"
f.password_confirmation "secret"
f.association :company
end
Factory.define :role do |f|
f.name "company_admin"
end
Factory.define :company do |f|
f.name "foobar"
f.vat_id "1234"
end
Upvotes: 3
Views: 2872
Reputation: 51697
Without changing your existing logic, I would test this logic in the user_spec like so:
describe User do
let!(:admin_role) { Role.create!(name: 'company_admin') }
let!(:company) { Company.create! }
it 'should be added to the default role when created' do
user = company.users.create!(name: 'Joe', email: '[email protected]')
user.should have(1).roles
user.roles.first.should == admin_role
end
end
Note: I would be using something like FactoryGirl for the admin role and company objects to make them reusable.
Your use of the role name to indicate behavior is not ideal. It will likely lead to lots of scattered logic throughout your application where you are finding the role by its name and checking the name with if/else or case statements. I would recommend using Rail's single table inheritance and moving all the admin role logic to a separate class. It will keep the model's logic cleaner and make testing much easier.
Upvotes: 0
Reputation:
I would approach it like this:
describe "#check_for_initial_company_admin_role" do
it "sets the first user created to be the administrator" do
company = Factory(:company)
user = Factory(:user)
company.users << user
user.roles.count.should > 0
user.roles.should include Role.find_by_name("company_admin")
end
end
An assumption here that may be incorrect is that you are using Factory Girl in your test framework. If not, that doesn't really change the "meat" of this test...just those first lines where you create the company and user.
You could also optionally check the user from the company side of things but honestly that feels like a different test entirely--one testing the association between those models.
The approach I would take is that since this is actually a model test you need to create and alter a real model objects, rather than mocking out those objects. If this were a controller test, I'd mock the models and stub the models aggressively.
I hope this helps and addresses your question. If not, let me know where I'm off base and I'll make another pass at it :) I'm only about a year into rspec but I've found that once I wrapped my head around how to test models vs. controllers I've come to love it.
Upvotes: 1