How to Stub Controller Methods in RSpec Request Specs! (FTW)
// Jun 18, 2015
Recently I’ve had to restructure our company’s API, and part of that was writing new request specs for everything.
One HUGE headache I ran into while writing request specs for the API was bypassing controller before_actions such as require_user, as well as stubbing out crucial controller instance methods like current_user.
I tried searching online; I don’t exaggerate when I say that I tried nearly everything:
You get the idea… Hours of searching Stack Overflow and Relish and different docs didn’t yield me the answer I needed.
Then I realized that current_user, require_login and all these methods were INSTANCE METHODS. Not class methods. Here’s the correct way to stub out instance methods in RSpec:
This RSpec helper allows you to stub out any instance method that exists on any class. However, my major use for this has been for stubbing out controller methods such as current_user, any controller before actions and access tokens if I’m doing request specs on a Rails API.
Example of controller action to test:
In this example, we are using the Doorkeeper gem to protect the controller against unauthorized API calls (the following example will work with other token authorization methods too, the process is similar).
So in this situation we would have to stub out the access token and the current user in order to successfully test the response of our request:
We have now succesfully stubbed out current_user and doorkeeper_token and can now successfully test our controller#action!
BUT WAIT. There’s a bunch of code used just for stubbing, it would suck if we had to repeat that code over and over again for every request spec that we create… So let’s move that into a spec support in order to write less code!
In the end, our spec will look like this:
It took me quite a while to figure this out but in the end it was more than worth it. Armed with this knowledge, I’m sure you’ll be able to bang out pages and pages of request specs in no time!
TL;DR Use allow_any_instance_of(Object).to receive(method_on_object).and_return(mock_method_used_for_testing) to mock an instance method on any object. Such as current_user on a controller.