Passing bearer token in RSPEC (no implicit conversion of nil into String) - ruby-on-rails

require 'rails_helper'
RSpec.describe "User management", :type => :request do
describe "Example::V2::Users" do
describe "GET /api/v2/users/" do
it 'returns status 200, authorized' do
#token = "Bearer 123"
#url = "https://api.example-v2.com/v2/users/me"
#headers = { "AUTHORIZATION" => #token}
get #url, as: :json, headers: {:Authorization => #token}
expect(response.status).to eq 200
end
end
end
end
I am trying to pass the #token but I am getting this error
Failure/Error: get #url, as: :json, headers: {:Authorization => #token}
TypeError: no implicit conversion of nil into String
I can make a get request without the params and headers and it works but as soon as I add params or headers I get the error, I even tried writing it like so
1 - get #url, {}, { Authorization: #token}
ArgumentError: wrong number of arguments (given 2, expected 1)
2 - get #url, params: {}, headers: { Authorization: #token}
TypeError: no implicit conversion of nil into String
Some smart people please point me in the right direction =).
Gems:
gem 'rspec-rails', '~> 3.8'
gem 'rails', '~> 6.0.2.2'

get #url, as: :json, headers: { Authorization: #token }
this works in my environment when type: reuqst.
I suggest you can use byebug just before the GET request
and check the #token and the #url.
If everything looks good.
Check the get if it can make request to root url?
Check the rails_helper if it requires any suspicious file?

Please check this its working for me.
require 'rails_helper'
RSpec.describe "User management", :type => :request do
describe "PeopleGoal::V2::Users" do
describe "GET /api/v2/users/" do
it 'returns status 200, authorized' do
request.headers["AUTHORIZATION"] = "Basic #{user.id}"
#url = "https://api.peoplegoal-v2.com/v2/users/me"
get #url, format: :json
expect(response.status).to eq 200
end
end
end
end

Related

How to pass authentication token in RSpec test for JSON API?

I'm trying to add an authorization token to an RSpec get JSON API test in Rails. But everything tried so far results in an error. The issue seems that the token is not being properly passed in the request.
expected the response to have a success status code (2xx) but it was 401
Current code:
Project_spec.rb (tests)
before do
#project = create(:project, :key => "123")
get '/api/v1/projects/1', {:Authorization => "Token 123"}, format: :json
end
it "returns correct status" do
expect( response ).to have_http_status(:success)
end
ProjectsController.rb
before_filter :restrict_access, :except => :create
def show
#project = Project.find(params[:id])
render json: #project
end
def restrict_access
authenticate_or_request_with_http_token do |token, options|
Project.exists?(key: token)
end
end
Based on a few recommended solution found online, I've tried
get '/api/v1/projects/1', format: :json, token: "123"
get '/api/v1/projects/1', { 'HTTP-AUTHORIZATION' => 'Token "123"' }, format: :json
get '/api/v1/projects/1', {:Authorization => "Token 123"}, format: :json
But nothing seems to successfully pass the authorization token.
Note: Style in #3 works when posting from an external application, but not in the RSpec test itself.
Does anyone have experience with this and can share some direction?
Use it like:
get '/api/v1/projects/1', {}, { Authorization: "Token 123"}
get is the http method, {} an empty params, { :Authorization => "Token 123"} headers
get(path, parameters = nil, headers_or_env = nil)
Documentation
Other way:
before do
# some code here
#request.env['Authorization'] = "Token 123"
get '/api/v1/projects/1', format: :json
end
It worked when I just tried like this
get '/api/v1/projects/1', as: :json, headers: {:Authorization => "Token 123"}

Setting token and header in rspec test for JSON API

I'm trying to test a json written in rails in rspec. I'm not sure about the syntax. This is what I have so far:
require 'rails_helper'
RSpec.describe V1::VersionsController, :type => :controller do
before do
#token = "0"
request.env["Content-Type"] = 'application/vnd.api+json; charset=utf-8'
end
describe "GET index" do
it "renders the index JSON" do
#request.headers['Content-Type'] = 'application/vnd.api+json; charset=utf-8'
request.env['Content-Type'] = 'application/vnd.api+json; charset=utf-8'
params = { token: #token }
get :index, :format => :json, token: #token
#response.should be_success
body = JSON.parse(response.body)
ap body
end
end
end
I tried it in a bunch of different ways as you can see. But I'm getting a 403 error.
I'm using Rails 5.0.0.beta3, ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux] and rspec-3.1.
So it turns out, the problem was not with my code, but the fact that my test database was not populated with the token. I had a rake task for this and just running it with RAILS_ENV=test solved the issue. Also the code in my question is not very clean as I'm trying a bunch of different stuff to achieve the same result. Here's the final spec for anyone who might be interested:
require 'rails_helper'
RSpec.describe V1::VersionsController, :type => :controller do
before do
#token = "0"
#request.headers['Content-Type'] = 'application/vnd.api+json; charset=utf-8'
end
describe "GET index" do
it "renders the index JSON" do
get :index, :format => :json, token: #token
body = JSON.parse(response.body)
expect(response.status).to eq 200
expect(body["meta"]["app_name"]).to eq Rails.application.class.parent_name
expect(body["meta"]["app_version"]).to eq ApplicationName.version
end
end
end

Can't stub request to Flickr API

I can't stub request to Flickr API for my
controller test. I use gem 'flickraw' for getting data from Flickr API.
flickr_search_controller.rb:
module Dashboard
class FlickrSearchController < Dashboard::BaseController
respond_to :js
def search
#search_tag = params[:search]
photos_list = if #search_tag.blank?
flickr.photos.getRecent(per_page: 10)
else
flickr.photos.search(text: #search_tag, per_page: 10)
end
#photos = photos_list.map { |photo| FlickRaw.url_q(photo) }
end
end
end
flickr_search_controller_spec.rb:
require 'rails_helper'
describe Dashboard::FlickrSearchController do
let(:user) { FactoryGirl.create(:user) }
before(:each) do
stub_request(:post, "https://api.flickr.com/services/rest").to_return(status: 200)
#controller.send(:auto_login, user)
end
describe 'when user didn\'t set search tag' do
it 'returns recend photo'do
get :search, search: ' '
expect(response.status).to eq(200)
end
end
end
I get in console next error:
Failures:
1) Dashboard::FlickrSearchController when user didn't set search tag returns recend photo
Failure/Error: flickr.photos.getRecent(per_page: 10)
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: POST https://api.flickr.com/services/rest/ with body 'method=flickr.reflection.getMethods&format=json&nojsoncallback=1' with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'OAuth realm="https://api.flickr.com/services/rest/", oauth_consumer_key="32904448e7d40c7e833c7b381c86cd31", oauth_nonce="lCL%2FUM9o8go5XNVy4F7p%2FNxHJrY%2BvFNLhlzueFq8Juc%3D", oauth_signature="1b77fc6af54b2b51%26", oauth_signature_method="PLAINTEXT", oauth_timestamp="1455128674", oauth_token="", oauth_version="1.0"', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'FlickRaw/0.9.8'}
You can stub this request with the following snippet:
stub_request(:post, "https://api.flickr.com/services/rest/").
with(:body => {"format"=>"json", "method"=>"flickr.reflection.getMethods", "nojsoncallback"=>"1"},
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'OAuth realm="https://api.flickr.com/services/rest/", oauth_consumer_key="32904448e7d40c7e833c7b381c86cd31", oauth_nonce="lCL%2FUM9o8go5XNVy4F7p%2FNxHJrY%2BvFNLhlzueFq8Juc%3D", oauth_signature="1b77fc6af54b2b51%26", oauth_signature_method="PLAINTEXT", oauth_timestamp="1455128674", oauth_token="", oauth_version="1.0"', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'FlickRaw/0.9.8'}).
to_return(:status => 200, :body => "", :headers => {})
============================================================
Does somebody have idea how can I stub this request?
Note the slash in uri, the request you're stubbing is not the one being made

no route matches error on route in rake routes

I'm testing a RESTful api in rails using rspec.
My request looks like this:
before(:each) do
#user = FactoryGirl.create(:user)
sign_in(#user)
end
it "returns a 200 code when a user checks a valid token" do
get "/api/v1/users/#{#user.id}/token_check", token: #user.authentication_token
expect(response.code).to eql(200)
end
when i run the testing suite, I receive the error:
Failure/Error: get "/api/v1/users/#{#user.id}/token_check", token: #user.authentication_token
ActionController::UrlGenerationError:
No route matches {:action=>"/api/v1/users/1/token_check", :controller=>"api/v1/users", :token=>"6fgswkHwWXrcyDQNJVBZ"}
# ./spec/controllers/api/v1/users_controller_spec.rb:32:in `block (2 levels) in <top (required)>'
However, I can see the route for this action in rake routes:
api_v1_user_token_check GET /api/v1/users/:user_id/token_check(.:format) api/v1/users#token_check {:format=>:son}
I match this to my users_controller#token_check. Here is my controller and action:
def token_check
render json: {
result: ['Your authentication token is valid'],
}, status: 200
end
No route matches {:action=>"/api/v1/users/1/token_check", :controller=>"api/v1/users", :token=>"6fgswkHwWXrcyDQNJVBZ"}
As the error message shows, you just need to specify the action name.
from get "/api/v1/users/#{#user.id}/token_check", token: #user.authentication_token
to get "token_check", user_id: #user.id, token: #user.authentication_token
I was getting this same error. I solved this issue by simply moving it statement inside :type => :request describe statement like below:
describe 'GET /v1/feeds/' , :type => :request do
get '/api/v1/cars/random', xhr: true, headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
expect(response.status).to eq 401
end
end

Set Rspec default GET request format to JSON

I am doing functional tests for my controllers with Rspec. I have set my default response format in my router to JSON, so every request without a suffix will return JSON.
Now in rspec, i get an error (406) when i try
get :index
I need to do
get :index, :format => :json
Now because i am primarily supporting JSON with my API, it is very redundant having to specify the JSON format for every request.
Can i somehow set it to default for all my GET requests? (or all requests)
before :each do
request.env["HTTP_ACCEPT"] = 'application/json'
end
Put this in spec/support:
require 'active_support/concern'
module DefaultParams
extend ActiveSupport::Concern
def process_with_default_params(action, parameters, session, flash, method)
process_without_default_params(action, default_params.merge(parameters || {}), session, flash, method)
end
included do
let(:default_params) { {} }
alias_method_chain :process, :default_params
end
end
RSpec.configure do |config|
config.include(DefaultParams, :type => :controller)
end
And then simply override default_params:
describe FooController do
let(:default_params) { {format: :json} }
...
end
The following works for me with rspec 3:
before :each do
request.headers["accept"] = 'application/json'
end
This sets HTTP_ACCEPT.
Here is a solution that
works for request specs,
works with Rails 5, and
does not involve private API of Rails (like process).
Here's the RSpec configuration:
module DefaultFormat
extend ActiveSupport::Concern
included do
let(:default_format) { 'application/json' }
prepend RequestHelpersCustomized
end
module RequestHelpersCustomized
l = lambda do |path, **kwarg|
kwarg[:headers] = {accept: default_format}.merge(kwarg[:headers] || {})
super(path, **kwarg)
end
%w(get post patch put delete).each do |method|
define_method(method, l)
end
end
end
RSpec.configure do |config|
config.include DefaultFormat, type: :request
end
Verified with
describe 'the response format', type: :request do
it 'can be overridden in request' do
get some_path, headers: {accept: 'text/plain'}
expect(response.content_type).to eq('text/plain')
end
context 'with default format set as HTML' do
let(:default_format) { 'text/html' }
it 'is HTML in the context' do
get some_path
expect(response.content_type).to eq('text/html')
end
end
end
FWIW, The RSpec configuration can be placed:
Directly in spec/spec_helper.rb. This is not suggested; the file will be loaded even when testing library methods in lib/.
Directly in spec/rails_helper.rb.
(my favorite) In spec/support/default_format.rb, and be loaded explicitly in spec/rails_helper.rb with
require 'support/default_format'
In spec/support, and be loaded by
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
which loads all the files in spec/support.
This solution is inspired by knoopx's answer. His solution doesn't work for request specs, and alias_method_chain has been deprecated in favor of Module#prepend.
In RSpec 3, you need make JSON tests be request specs in order to have the views render. Here is what I use:
# spec/requests/companies_spec.rb
require 'rails_helper'
RSpec.describe "Companies", :type => :request do
let(:valid_session) { {} }
describe "JSON" do
it "serves multiple companies as JSON" do
FactoryGirl.create_list(:company, 3)
get 'companies', { :format => :json }, valid_session
expect(response.status).to be(200)
expect(JSON.parse(response.body).length).to eq(3)
end
it "serves JSON with correct name field" do
company = FactoryGirl.create(:company, name: "Jane Doe")
get 'companies/' + company.to_param, { :format => :json }, valid_session
expect(response.status).to be(200)
expect(JSON.parse(response.body)['name']).to eq("Jane Doe")
end
end
end
As for setting the format on all tests, I like the approach from this other answer: https://stackoverflow.com/a/14623960/1935918
Perhaps you could add the first answer into spec/spec_helper or spec/rails_helper with this:
config.before(:each) do
request.env["HTTP_ACCEPT"] = 'application/json' if defined? request
end
if in model test (or any not exist request methods context), this code just ignore.
it worked with rspec 3.1.7 and rails 4.1.0
it should be worked with all rails 4 version generally speaking.
Running Rails 5 and Rspec 3.5 I had to set the headers to accomplish this.
post '/users', {'body' => 'params'}, {'ACCEPT' => 'application/json'}
Thi matches what the example in the docs looks like:
require "rails_helper"
RSpec.describe "Widget management", :type => :request do
it "creates a Widget" do
headers = {
"ACCEPT" => "application/json", # This is what Rails 4 accepts
"HTTP_ACCEPT" => "application/json" # This is what Rails 3 accepts
}
post "/widgets", { :widget => {:name => "My Widget"} }, headers
expect(response.content_type).to eq("application/json")
expect(response).to have_http_status(:created)
end
end
Per the Rspec docs, the supported method is through the headers:
require "rails_helper"
RSpec.describe "Widget management", :type => :request do
it "creates a Widget" do
headers = {
"ACCEPT" => "application/json", # This is what Rails 4 and 5 accepts
"HTTP_ACCEPT" => "application/json", # This is what Rails 3 accepts
}
post "/widgets", :params => { :widget => {:name => "My Widget"} }, :headers => headers
expect(response.content_type).to eq("application/json")
expect(response).to have_http_status(:created)
end
end
For those folks who work with request tests the easiest way I found is to override #process method in ActionDispatch::Integration::Session and set default as parameter to :json like this:
module DefaultAsForProcess
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: :json)
super
end
end
ActionDispatch::Integration::Session.prepend(DefaultAsForProcess)
Not sure if this will work for this specific case. But what I needed in particular was to be able to pass a params hash to the post method. Most solutions seem to be for rspec 3 and up, and mention adding a 3rd parameter like so:
post '/post_path', params: params_hash, :format => 'json'
(or similar, the :format => 'json' bit varies)
But none of those worked. The controller would receive a hash like: {params: => { ... }}, with the unwanted params: key.
What did work (with rails 3 and rspec 2) was:
post '/post_path', params_hash.merge({:format => 'json'})
Also check this related post, where I got the solution from: Using Rspec, how do I test the JSON format of my controller in Rails 3.0.11?
Why don't RSpec's methods, "get", "post", "put", "delete" work in a controller spec in a gem (or outside Rails)?
Based off this question, you could try redefining process() in ActionController::TestCase from https://github.com/rails/rails/blob/32395899d7c97f69b508b7d7f9b7711f28586679/actionpack/lib/action_controller/test_case.rb.
Here is my workaround though.
describe FooController do
let(:defaults) { {format: :json} }
context 'GET index' do
let(:params) { defaults }
before :each do
get :index, params
end
# ...
end
context 'POST create' do
let(:params) { defaults.merge({ name: 'bar' }) }
before :each do
post :create, params
end
# ...
end
end

Resources