This should be simple, but i can't get it to work. I want to stub an :
#alliance.save
so that it returns true. I tried :
Alliance.stub(:save).and_return(true)
but it won't work. Any ideas ?
If I'm not mistaken, Alliance.stub(:save) would affect calls to Alliance.save. You want #alliance.stub(:save).and_return(true).
Mocha has a useful method any_instance, so you could do something like Alliance.any_instance.stubs(:save).returns(true), which would (as the name implies) stub the save method for any instance of Alliance.
Using the new RSpec syntax:
allow_any_instance_of(Alliance).to receive(:save).and_return(true)
You're probably looking for something like:
describe AllianceController do
let(:alliance) { mock_model(Alliance) }
describe "#<controller action>" do
before do
Alliance.stub :new => alliance
end
context "valid alliance" do
before do
alliance.stub :save => true
end
it "should ..." do
end
end
end
end
The inner context allows you to work with an Alliance mock which has the save method stubbed to return true.
Related
In my Rails application I have a User model:
class User
def self.foo
User.all.each{ |user| user.bar }
end
def bar
end
end
In my spec file I want to check that foo calls bar for every user, so far that's what I have:
describe '::foo' do
let!(:users) { Fabricate.times(5, :user) }
it 'calls bar for every user' do
users.each do |user|
expect(user).to receive(:bar)
end
User.foo
end
end
Although the method gets called (I debugged it, so I'm sure of that) the spec is red.
Also I tried to write this code to understand where the problem was:
let!(:user) { Fabricate(:user) }
it 'fails' do
expect(user).to receive(:bar)
User.first.bar
end
it 'pass' do
expect(user).to receive(:bar)
user.bar
end
It seems that if I reference my instance directly it works, if I obtain it from the DB the expectation doesn't work.
I use mongoid, not sure if this is relevant.
I believe it cannot be done due to how RSpec works: When you set an expectation, RSpec essentially 'wraps' the object so that it can keep track of the messages it receives.
But when the implementation code fetches records from the database, they are not wrapped, so RSpec isn't able to record their messages.
RSpec does have a method allow_any_instance_of which can help in some cases, but its use is discouraged, and don't think it would be suitable here.
In this situation, I would suggest stubbing User.all to return some doubles (two should be sufficient). You can then verify that bar is called on each one.
I am in the process of refactoring a bloated controller that serves a polymorphic model for carousels. I am trying to build a class method that handles finding and returning the item that is carouselable.
In my RSPEC tests I want to stub the method, 'is_something?' on the venue that is found as a result of the params.
def self.find_carouselable(params)
.......
elsif params[:venue_id].present?
venue=Venue.friendly.find(params[:venue_id])
if venue.is_something?
do this
else
do that
end
end
end
I cant work out how to stub an object that is created as a result of the inputted data - I am not sure if this is called stubbing or mocking?
context "carouselable is a venue" do
before do
allow(the_venue).to receive(:is_something?).and_return(true)
end
it "returns the instance of the carouselable object" do
expect(CopperBoxCarouselItem.find_carouselable(venue_params)).to eq the_venue
end
end
many thanks
You should be able to do:
allow_any_instance_of(Venue).to receive(:is_something?).and_return(true)
https://www.relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations/allow-a-message-on-any-instance-of-a-class
You only need to stub the Venue bit, like so
before do
allow(Venue).to receive(:friendly).and_return(some_venues)
allow(some_venues).to receive(:find).and_return(venue)
allow(venue).to receive(:is_something?).and_return(true)
end
In my Rails application I've created a method that creates a model hierarchy basing on JSON data. I'd like to assure that the method does not save anything to the database. I know I can write test like:
expect {
Importer.import(json)
}.not_to change(Model1, :count)
expect {
Importer.import(json)
}.not_to change(Model2, :count)
# etc.
But I'd like to do it in more generic way. Is there a method in RSpec to check if any model had been saved during a test?
If the "import" method can return a Model object, you can write expectation like this:
imported_obj = Importer.import(json)
expect(imported_obj).not_to be_persisted
The "be_persisted" is a predicate-matcher, it simply call persisted? method on imported_obj. The method "persisted?" is a Rails' method, which return true if imported_obj has been saved.
The way you are doing it is not too bad, though it is a bit verbose.
You could use the have matcher.
Importer.import(json)
collection1 = Model.all
collection1.should have(5).items
...
collection2.should_not have(5).items
# etc....
Though, for your context, reading it like your original test makes more sense (outside of creating a custom matcher that really makes sense). Your testing what the "Importer" is expected to do or not do.
have(n).items matcher
Not sure if Rspec could help you with this, but a quick manual trip to the database might do the job?
[Model1, Model2].each do |model|
model.where('updated_at > ?', Time.now - 2.seconds).count.should eq 0
end
I have this class:
class EnablePost
def initialize(post_klass, id)
raise "oops" if post_klass.blank?
#post_klass = post_klass
#id = id
end
def perform
post = #post_klass.find_by_id(#id)
return unless post
post.update_attribute :enabled, true
end
end
The spec I have to write to test the above:
describe EnablePost do
it "should enable a post" do
post = mock
post.should_receive(:blank?).and_return(false)
post.should_receive(:find_by_id).with(22).and_return(post)
post.should_receive(:update_attribute).with(:enabled, true)
result = EnablePost.new(Post, 22).perform
result.should be_true
end
end
But what I really want to do is treat EnablePost as a black box. I don't want to have to mock :blank?, :find_by_id or :update_attribute.
That is to say I want my spec to look like:
describe EnablePost do
it "should enable a post" do
post = mock
result = EnablePost.new(post, 22).perform
result.should be_true
end
end
What am I missing here? Am I using mocks incorrectly?
Yes, you're confusing mocks and stubs.
A good mock explanation: http://jamesmead.org/talks/2007-07-09-introduction-to-mock-objects-in-ruby-at-lrug/
Mocks:
Different things to different people
Ambiguous terminology
Confusion with Rails “mocks”
Mock Object:
Expected method invocations set in advance
Verifies actual invocations match expected ones
Also check out http://martinfowler.com/articles/mocksArentStubs.html [thanks to user Zombies in the comments]
If you're using RSpec, it aliases double, mock, and stub. RSpec expects you to choose whichever method name makes your code clearest.
Your first chunk of test code is using the word "mock" correctly. You're setting up the method invocations that you expect to be called, in advance, and then performing them.
However, you're testing two different areas of your code: the first area is the initialize method, the second is the #perform method.
You may find it easier to mock and stub if you write smaller methods:
# What you want to test here is the raise and the member variables.
# You will stub the post_klass.
def initialize(post_klass, post_id) # post_id is a better name
raise "oops" if post_klass.blank?
#post_klass = post_klass
#post_id = post_id # because we don't want to mask Object#id
end
attr_accessor :post_id
attr_accessor :post_klass
# What you want to test here is the post_klass calls #find_by_id with post_id.
# See we've changed from using instance variables to methods.
def post
post_klass.find_by_id(post_id)
end
# What you want to test here is if the update happens.
# To test this, stub the #post method.
def perform
p = post
return unless p
p.update_attribute :enabled, true
end
When you write your code this way, you make it easy to stub the #post method.
See this for RSpec example source code showing the difference between mock and stub:
http://blog.firsthand.ca/2011/12/example-using-rspec-double-mock-and.html
I'm newbie with rspec and I'm facing some problems with it. Could someone help me?
I have a controller action responsible for deactivate an user. I'm trying to cover it with rspec tests, but the result is not what I'm waiting for.
Controller:
def deactivate
#user = User.find(params[:id])
if !#user.nil?
#user.update_attribute(:active, false)
redirect_to users_url
end
end
Controller Spec
describe "PUT #deactivate" do
describe "with valid parameters" do
before (:each) do
#user = mock_model(User, :id => 100, :login => "login", :password => "password123",
:email => "email#gmail.com", :active => true)
User.should_receive(:find).with("100").and_return(#user)
end
it "should deactivate an user" do
#user.stub!(:update_attribute).with(:active, false).and_return(true)
put :deactivate, :id => "100"
#user.active.should eq false
end
end
end
The test result:
1) UsersController PUT #deactivate with valid parameters should deactivate an user
Failure/Error: #user.active.should eq false
expected: false
got: true
(compared using ==)
So, I don't understand why the active attribute stills true when it should be false. Any ideas ?
Thanks!
You appear to be stubbing the update_attribute method unnecessarily. Try removing that line and see what happens.
I look for this for a long time, update_column can always work no matter you use let or build
Your expectation is "wrong".
Let's see what happens when your spec it "should deactivate an user" is executed:
#user.stub!(:update_attribute).with(:active, false).and_return(true) modifies the existing mock model, so it has an update_attribute which, when called with arguments :active and false
will return true
will keep track that this call has happened (that's what mocks do)
(and, unlike a real User object, will do nothing else)
put :deactivate, :id => "100" calls the real deactivate in your Controller
Your Controller calls User.find. But you've mocked that class method, which will return the mock object #user instead of searching for the actual user with that id.
Your Controller calls #user.update_attribute. But because of step 3 above, #user here is the mock object, too. Its update_attributes method is the one from step 1. As we've seen above, it will return true, keep track that this call happened and do nothing else. Which means it will not change #user's active attribute, so that stays true.
Changing active when update_attribute is called is functionality of objects of the actual User class, but no such object came into play while running your spec. Because this functionality is inherited from ActiveRecord, you don't have to test it. Instead just test that the update_attribute has been received by the mock object:
it "should deactivate an user" do
#user.stub!(:update_attribute).with(:active, false).and_return(true)
put :deactivate, :id => "100"
#user.should have_received(:update_attribute).with(:active, false)
end
(I'm guessing about the old should syntax here, based on how it's done with the newer expect syntax.)
To mock or not?
If you do want to test the combined functionality of your controller with the actual User implementation, do not mock User or its objects. Instead test from the browser perspective with a request spec. (It might make sense to do that additionally, even if you want the isolated tests for only controller (with model mocked) and for only model (which probably won't require doubles, except maybe for other models).
Can you try this:
describe "should deactivate an user" do
before do
#user.stub!(:update_attribute).with(:active, false).and_return(true)
put :deactivate, :id => "100"
end
it { #user.active.should eq false }
end
when you are mocking the call to update_attribute, how is the model going to change?
if you are a beginner: DONT use stubs and mocks!
first get a general knowledge in testing, THEN expand your knowledge to mocks and stubs.