Stubbing controller actions in RSpec request specs
I ended up stubbing the controller method using any_instance
.
it "Should return 500 upon server error" do
UsersController.any_instance.stub(:index).and_raise(ArgumentError)
get "/users.json"
response.code.should eq("500")
response.body.should have_json_path("error")
end
Note:
Stubbing controller methods in a request spec doesn't make sense. But... In this case I am using the request spec suite as the acceptance criteria. One of the requirement was to ensure, all the error codes and messages match the API design.I was able to induce the server to raise all the HTTP error codes specified in the API design. Only edge case was the internal server error(i.e. 500)
. I had no means of inducing the controller to raise this error. Since, I am testing error reply and since this reply is independent of the location and source of the exception I decided to stub it.
RSpec: Stub controller method in request spec
You're supposed to be on vacation.
I think the right way is to avoid stubbing as much as you can in a request spec, doorkeeper needs a token to authorize so I'd do something like:
describe 'Items', type: :request do
describe 'GET /items' do
let(:application) { FactoryBot.create :oauth_application }
let(:user) { FactoryBot.create :user }
let(:token) { FactoryBot.create :access_token, application: application, resource_owner_id: user.id }
before do
get '/items', access_token: token.token
@parsed_body = JSON.parse(response.body)
end
it 'includes all of the items' do
expect(@parsed_body).to include(item_1)
expect(@parsed_body).to include(item_2)
end
end
end
Here are some examples of what those factories might look like.
Lastly, nice SO points!
Rspec: stub out controller action
You won't be able to stub a controller action from within a request spec. However you can stub the AJAX request, so that the controller action is never reached. Webmock is a good choice.
Correct way of stubbing method call in RSpec request
The point is that authenticate_user
assigns user to the variable (and you use it later). Please try:
allow(DecodeAuthenticationCommand).to receive_message_chain(:call, :result).and_return(user)
With the test double, you will have to define all methods for the user, such as contracts
. Also, you are checking if the contract was created - in my opinion, it is perfectly fine to use a real object for the user
.
Stubbing authentication in request spec
A request spec is a thin wrapper around ActionDispatch::IntegrationTest
, which doesn't work like controller specs (which wrap ActionController::TestCase
). Even though there is a session method available, I don't think it is supported (i.e. it's probably there because a module that gets included for other utilities also includes that method).
I'd recommend logging in by posting to whatever action you use to authenticate users. If you make the password 'password' (for example) for all the User factories, then you can do something like this:
def login(user)
post login_path, :login => user.login, :password => 'password'
end
RSpec stubbing only works for first request, does not work for subsequent requests
Finally discovered the problem.
In test.rb, config.cache_classes was set to false (no idea why since it defaults to true).
You want config.cache_classes to be set to true in testing environments, otherwise classes will be reloaded on every request and its methods will override the stub.
Rspec: stubbing controller methods ineffective (before_filter, private)
The solution is to use a method other than visit
in the spec. Apparently, visit
is intended for feature specs only. Instead, use get
or post
etc.
Controller Specs vs Request Specs?
Rails 3 & 4
Controller specs - A controller spec is an RSpec wrapper for a Rails functional test. It allows you to simulate a single http request in each example, and then
specify expected outcomes
Request specs - Request specs provide a thin wrapper around Rails' integration tests, and are
designed to drive behavior through the full stack, including routing
(provided by Rails) and without stubbing (that's up to you).
So if you want to test API controllers I would recommend to use Controller specs
as you are testing single requests.
Rails 5+
Rails 5 improved the speed and realism of request specs over Rails version 4's controller and request specs. The official recommendation of the Rails team and the RSpec core team is to write request specs instead (of controller specs).
Related Topics
Idiomatically Mock Openuri.Open_Uri with Minitest
Shading Mask Algorithm for Radiation Calculations
How to Add Iedriverserver to Path
Openssl::Cipher::Ciphererror When Running Staging Db on Local
How to Turn Off Bigint Primary Keys in Rails 5.1
Ruby Syntax Question: Rational(A, B) and Rational.New!(A, B)
Rvm and Gems, Bundle Show and Gem List
Gem::Ext::Builderror: Error: Failed to Build Gem Native Extension. on Cenos 6.5
Accessing a Value in a Method Using *
How to Upgrade Rvm When the Official Way Doesn't Work
How to Install Ruby System-Wide Using Rbenv
Ruby: Looking for Ruby-Embeddable Interpreter or Scripting Language
Open-Uri Is Not Redirecing Http to Https
Permanently Switching User in Capistrano 3 (Separate Authorization & Deploy)
Error: Error Installing JSON: Error: Failed to Build Gem Native Extension
Rails 4 Wysiwyg Bootsy Not Displaying Formatting
Error: Null Value in Column "Id" Violates Not-Null Constraint