Testing a model spec that uses an after_create callback
Here is a model that I'm using, I've simplified it a bit down to the simplest form that still fails my example:
class User < ActiveRecord::Base
after_create :setup_lists
def setup_lists
List.create(:user_id => self.id, :name => "current")
List.create(:user_id => self.id, :name => "master")
end
end
And I'd like to spec the example as follows:
require 'spec_helper'
describe User do
before(:each) do
@user = Factory(:user)
end
describe "#setup_lists" do
before(:each) do
List.stub(:create).with(:name => "current")
List.stub(:create).with(:name => "master")
it "creates a new master list" do
List.should_receive(:create).with(:name => "master")
end
it "creates a new current list" do
List.should_receive(:create).with(:name => "current")
end
end
end
Which I expected would work just fine, but I am left with the following error:
Failures:
1) User#setup_lists creates a new master list
Failure/Error: List.should_receive(:create).with(:name => "current")
(<List(id: integer, name: string, cre开发者_如何转开发ated_at: datetime, updated_at: datetime, user_id: integer) (class)>).create({:name=>"current"})
expected: 1 time
received: 0 times
# ./spec/models/user_spec.rb:44
2) User#setup_lists creates a new current list
Failure/Error: List.should_receive(:create).with(:name => "master")
(<List(id: integer, name: string, created_at: datetime, updated_at: datetime, user_id: integer) (class)>).create({:name=>"master"})
expected: 1 time
received: 0 times
# ./spec/models/user_spec.rb:48
Can anybody help me understand why this is happening?
Three issues:
1) The User object is created before setting the message expectation, so should_receive
never gets to see the message;
2) You're stubbing out methods for which you're also setting expectations. You want to stub out methods for which you don't set expectations, but which are needed for the test to pass
3) You need to pass in all the parameters
To fix, create the User object after setting the expectaion, and stub out each method in turn (because your model calls List.create twice):
describe User do
describe "#setup_lists" do
it "creates a new master list" do
List.stub(:create).with(:user_id=>1,:name => "current")
List.should_receive(:create).with(:user_id=>1,:name => "master")
@user = User.create
end
it "creates a new current list" do
List.stub(:create).with(:user_id=>1,:name => "master")
List.should_receive(:create).with(:user_id=>1,:name => "current")
@user = User.create
end
end
end
While it's really an issue of style, it makes more sense to use a real User object here rather than a factory, since you're testing the model itself.
zetetic's answer is awesome, but if you want something a bit quicker (and still works), I'd recommend using the shoulda-callback-matchers gem. It's a complete set of matchers that make testing callbacks easier. I'm all about easy & reducing boilerplate. You can see some examples in my RSpec model testing skeleton if you care to look.
Either way gets the job done!
精彩评论