How to make a class object instance to return a predefined value?
I am using Ruby on Rails 3.1.0 and the rspec-rails 2 gem. I am testing my controller code (BTW: I am newbie to rspec) and I would like to make possible that a class object instance returns a predefined value. That is, in my controller I have:
def create
...
if @current_user.has_authorization?
...
else
...
end
end
In order to test the "else
part" of the if
statement I would like to make possible that (for the current spec example that I am working on - read below for a sample implementation) the @current_开发者_JS百科user.has_authorization?
returns false
.
How can I make that?
I tried the following in my spec file, but it seems do not work as expected:
it "should have no authorization" do
@current_user.stub(:has_authorization?).and_return(false)
# I also tried the following and it still doesn't work
# @current_user.should_receive(:has_authorization?).and_return(false)
post :create
...
end
@current_user
in the context of your rspec test is not the same as @current_user
in the context of your controller. One is an instance variable, in the instance of your controller class that Rails is running. The other is an instance variable in your rspec test.
You aren't supposed to poke at your user variable, but rather need to make it so Rails finds a User supplied by the test framework. Take a look at this example.
To avoid that issue you could use mocha, to do that do the following:
Add the mocha gem to your Gemfile
and run bundle install
.
Then change the Rspec mock configuration framework in your spec_helper.rb
file
RSpec.configure do |config|
config.mock_with :mocha
# config.mock_with :rspec
end
Then in your spec you can do this
it "should have no authorization" do
User.any_instance.stubs(:has_authorization?).returns(false)
post :create
...
end
This (or the correct answer from Coderer) will solve your issue.
Notice that as far as I know there is no any_instance
equivalent in Rspec mocking functions.
When your spec is running, self
is pointing to a test object (try p self
or puts self.class
inside a spec sometime and see for yourself), so @current_user
is referring to an instance variable on that test object. It has nothing whatsoever to do with the @current_user
instance variable on your controller.
One way to do what you want is to create a current_user method or accessor on your controller, and call it instead of accessing @current_user
directly. Then you can stub or mock that method from your spec, assuming you can get a pointer to your controller instance.
Another way is to set the @current_user
variable directly from inside your spec. Again, get a pointer to your controller instance, then do x = mock("user"); controller.instance_variable_set(:@current_user, x)
then you can mock/stub methods on x
.
精彩评论