Stub global cookies variable in Rspec Rails test - ruby-on-rails

The Rspec test looks like this:
RSpec.describe Api::UsersController, type: :controller do
describe 'POST #create' do
subject { post :create, params: create_params }
context '...' do
In the controller, I use:
cookies.permanent[:foo] == 'bar'
However, I'm getting:
NameError:
undefined local variable or method `cookies' for #<Api::UsersController:0x000000137702f0>
Another SO question has an answer that says to add type: :request to describe, but this causes other errors. I'd rather just completely stub out cookies so cookies.permanent[:foo] == 'bar' is always false. How do I do that?

By default controllers in rails-api don't include the middleware that handles cookies. If you need cookies then you need to add that middleware:
config.middleware.use ActionDispatch::Cookies
For the above problem, you need to include
ActionController::Cookies
in your spec files

Related

RSpec: Stub controller method in request spec

I'm writing an RSpec request spec, which looks roughly like (somewhat shortened for brevity):
describe 'Items', type: :request do
describe 'GET /items' do
before do
allow_any_instance_of(ItemsController).to receive(:current_user).and_return(user)
get '/items'
#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
The controller looks like:
class ItemsController < ApplicationController
before_action :doorkeeper_authorize!
def index
render(json: current_user.items)
end
end
As you can see, I'm trying to stub doorkeeper's current_user method.
The tests currently pass and the controller works as expected. My question is about the line:
allow_any_instance_of(ItemsController).to receive(:current_user).and_return(user)
I wrote this line based on the answers in How to stub ApplicationController method in request spec, and it works. However, the RSpec docs call it a "code smell" and rubocop-rspec complains, "RSpec/AnyInstance: Avoid stubbing using allow_any_instance_of".
One alternative would be to get a reference to the controller and use instance_double(), but I'm not sure how to get a reference to the controller from a request spec.
How should I write this test avoid code smells / legacy testing approaches?
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!
have you thought not to mock current_user at all?
if you write a test helper to sign in a user before your request spec, current_user will be populate automatically as if it was a real user. The code would look like this:
before do
sign_in user
get '/items'
#parsed_body = JSON.parse(response.body)
end
if you are using devise gem for authentication it has a nice written wiki page about that here.
This approach is also recommended here by #dhh

Rspec producing a undefined method `route_to' , despite having rspec/rails defined in gemfile/rails helper

QUESTION: What have I done wrong that the route_to method remains undefined?
I'm very new to this but I'm trying to develop some route tests via the rspec gem.
My issue is that I am obtaining the error:
undefined method `route_to' for #<RSpec::ExampleGroups::RouteToHomepage
I have already looked through the API for this query, and I've already done the following:
Install gem 'rspec-rails'
In rails_helper.rb
require 'rspec/rails'
In my routing_spec.rb (where I am writing the routes)
require 'rails_helper'
describe "route to homepage" do
it "routes /home to index" do
expect(:get => "/homes").to route_to(
action: "index"
)
end
end
What exactly do I need to change or add, so the "route_to" method is defined? I've already read around and apparently it's defined in the "rspec-rails" gem, which I have, and already included.
From the documentation:
Routing specs are marked by :type => :routing or if you have set
config.infer_spec_type_from_file_location! by placing them in spec/routing.
You didn't say where the routing_spec.rb is located, but if it's inside the folder spec/routing/ then you could choose to enable the above config option.
Otherwise, or in general, you must do this:
require 'rails_helper'
describe "route to homepage", type: :routing do
it "routes /home to index" do
expect(:get => "/homes").to route_to(
action: "index"
)
end
end
Doing this will include the necessary RSpec helper that defines route_to, among other methods.

RSpec, authenticating Devise user in request specs

I'm trying to write RSpec request specs in order to test my service API and for that I need the user to be authenticated.
I found some examples on the net but nothing works, for the moment I'm stuck with this:
require "spec_helper"
include Warden::Test::Helpers
Warden.test_mode!
describe "My requests" do
it "creates an imaginary object" do
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
post "/my_service", :my_data=> {:some => "data"}
expect(response.body).to include("success")
end
end
And the error I'm getting is:
ArgumentError: uncaught throw :warden
Thank you for your help.
It is simplest to just:
spec/rails_helper.rb
RSpec.configure do |config|
# ...
config.include Devise::Test::IntegrationHelpers, type: :request
end
And just use sign_in in your request spec. This is the equivalent of declaring include Devise::Test::IntegrationHelpers in an system/feature spec or Rails system/controller test.
Doing it this way leads to a 'better' test.
You need to actually sign in the user (i.e. the user needs to submit the login form, or at least do a POST on your login action) as explained here: Stubbing authentication in request spec

problem with rspec test, undefined method 'post'

I am writing a spec to test the behavior of the mashup_controller when someone sends a query through a URL. I need to simulate the parameters contained in the URL, and i read that the post() method will do that, however when i get an error:
1) MashupController simulates query
Failure/Error: post :create
NoMethodError:
undefined method `post' for
#<RSpec::Core::ExampleGroup::Nested_1:0x980bc50>
# ./mashup_controller_rspec.rb:9:in `block (2 levels) in <top (required)>'
Finished in 0.20199 seconds 1 example, 1 failure
Failed examples:
rspec ./mashup_controller_rspec.rb:7 # MashupController simulates query
Here is my code:
require 'spec_helper'
require 'mashup_controller.rb'
describe MashupController do
it "simulates query" do
post :create
end
end
Sorry if I'm not making any sense. I am very new to rails and rspec. Any help would be appreciated. Thanks.
If the spec file is not under spec/controllers, methods like get and post will not be automatically made available by rspec-rails.
You either need to tag your spec:
describe MyController, type: :controller do
# ...
end
or include the module:
describe MyController do
include RSpec::Rails::ControllerExampleGroup
# ...
end
See the relevant code in rspec-rails.
Make sure you have gem spec-rails in your Gemfile
Your mashup_controller_rspec.rb should be under spec/controllers
I used gem rspec-rails instead of gem spec-rails.
In Rails 4, you can declare the type of the RSpec tests as :request and the spec file can be in any directory.
example: in spec/routes/users.rb
RSpec.describe 'UserRoutes', type: :request do
...
end
My solution is
describe MyController, type: :controller
...
end

RSpec Rails Login Filter

I recently switched started using rspec-rails(2.6.1) with my Rails(3.0.8) app. I'm used to Test::Unit, and I can't seem to get a filter working for my test methods. I like to keep things as DRY as possible, so I'd like to set up a filter that I can call on any test method that will login as an Authlogic user before the test method is called. I tried accomplishing this by using an RSpec filter in spec_helper.rb:
config.before(:each, :login_as_admin => true) do
post "/user_sessions/create", :user_session => {:username => "admin", :password => "admin"}
end
Then I use it in the corresponding test method(in this case spec/controllers/admin_controller_spec.rb):
require 'spec_helper'
describe AdminController do
describe "GET index" do
it("gives a 200 response when visited as an admin", :login_as_admin => true) do
get :index
response.code.should eq("200")
end
end
end
However, I get this error when I run rspec spec:
Failures:
1) AdminController GET index gives a 200 response when visited as an admin
Failure/Error: Unable to find matching line from backtrace
RuntimeError:
#routes is nil: make sure you set it in your test's setup method.
Blech. Can I only send one HTTP request per test? I also tried stubbing out my authenticate_admin method(inside the config.before block), without any luck.
Unfortunately, there is no way at the moment to do what you're trying to do in a globally defined before hook. The reason is that before hooks are executed in the order in which they get registered, and those declared in RSpec.configure are registered before the one that rspec-rails registers internally to set up the controller, request, response, etc.
Also, this has been reported to https://github.com/rspec/rspec-rails/issues/391.
You should use shulda's macrons. To use shoulda modify your spec_helper.rb
RSpec.configure do |config|
config.include Clearance::Shoulda::Helpers
end
And then can setup filter in controller spec like
require 'spec_helper'
describe AdminController do
fixture :users
before(:each) do
sign_in_as users(:your_user)
end
describe "GET index" do
it("gives a 200 response when visited as an admin", :login_as_admin => true) do
get :index
response.code.should eq("200")
end
end
end

Resources