undefined method `response' for nil:NilClass rspec test - ruby-on-rails

I created a test, and for some reason the should is run on a nil type.
I am using rails 4.2 and rspec-rails 3.1.0. I am not sure what I am doing wrong - this is the test, and the error is on the last it { should respond_with 401 } test
require 'rails_helper'
class Authentication
include Authenticable
def request
end
def response
end
end
describe Authenticable do
let(:authentication) { Authentication.new }
describe "#current_user" do
before do
#user = FactoryGirl.create :user
request.headers["Authorization"] = #user.auth_token
allow(authentication).to receive(:request).and_return(request)
end
it "returns the user from the authorization header" do
expect(authentication.current_user.auth_token).to eql #user.auth_token
end
end
describe "#authenticate_with_token" do
before do
#user = FactoryGirl.create :user
allow(authentication).to receive(:current_user).and_return(nil)
allow(response).to receive(:response_code).and_return(401)
allow(response).to receive(:body).and_return({"errors" => "Not authenticated"}.to_json)
allow(authentication).to receive(:response).and_return(response)
end
it "render a json error message" do
expect(json_response[:errors]).to eql "Not authenticated"
end
it { should respond_with 401 }
end
end

it { should respond_with 401 } does not specify which object should repond with 401, that's why the error.
To fix it try:
expect(response).to respond_with 401
or
use subject:
subject{ response }
it { should respond_with 401 }

subject { authentication }
you should put this line as below
let(:authentication) { Authentication.new }
subject { authentication }
describe '#current_user' do

Related

How make stub on request with VCR?

How can I do stub on request with VCR?
The problem is that real request is made in the test, which I want to stub.
RSpec.describe CreditRegisterLoader do
describe ".call" do
it "should create credit institutions", :vcr do
Timecop.freeze(2020, 3, 25, 13, 0, 0) do
expect { described_class.new.call }.to change { CreditInstitution.count }.by(4965)
end
end
end
end
Also here is code of my class CreditRegisterLoader which I want to test:
class CreditRegisterLoader < ApplicationService
def initialize
#timestamp = (Time.now - 1.minute).to_i.to_s
end
def call
sai_response = get_credit_institutions
unless sai_response
Airbrake.notify("invalid_sai_response")
return
end
begin
CreditInstitutionUpdater.new(JSON.parse(sai_response.body)).create
rescue => error
Airbrake.notify(error)
end
end
private
def get_credit_institutions
RestClient::Request.execute(
method: :post,
url: "https://sai.dpl.europa.eu/register/api/search/entities?t=#{#timestamp}",
headers: {
"Content-Type" => "application/json",
"Accept" => "application/json",
},
payload: JSON.generate({"$and": [{"_messagetype": "SAIDPL"}]})
)
end
end
I would suggest the following solution
RSpec.describe CreditRegisterLoader do
describe ".call" do
let(:response) { OpenStruct.new(body: File.read("yours fixtures in json format")) }
context "Failure flow" do
it "should notify Airbrake with error" do
error = StandardError.new("Bad Request")
expect(RestClient::Request).to receive(:execute).and_return(response)
expect_any_instance_of(CreditInstitutionUpdater).to receive(:create).and_raise(error)
expect(Airbrake).to receive(:notify).with(error)
subject.call
end
it "should notify Airbrake with invalid_sai_response" do
expect(subject).to receive(:get_credit_institutions).and_return(nil)
expect(Airbrake).to receive(:notify).with("invalid_sai_response")
subject.call
end
end
context "Successfully flow" do
it "should creates credit institutions" do
expect(RestClient::Request).to receive(:execute).and_return(response)
expect { subject.call }.to change { CreditInstitution.count }.by(2)
fixtures_response = JSON.parse(response.body)
end
end
end
end

Rails RSpec use mock instead of stub

I've got a lib which deals with some external API. The details are irrelevant, everything works well:
module SampleApi
extend self
def fetch_mm_recommender(email:)
handle_response(client.get("#{Sample.test_uri}/api/v1/mm_recommender", email: email))
end
private
def handle_response(response)
return parse_body(response) if response.success?
Rails.logger.error "Request failed with code: #{response.status}, message: #{response.reason_phrase}"
response.reason_phrase
end
def parse_body(response)
JSON.parse(response.body)
end
def client
#client ||= HttpHelper.new(with_cert: true)
end
end
I've made RSpec test for that:
describe '#fetch_mm_recommender' do
subject(:fetch_physician) { described_class.fetch_mm_recommender(email: email) }
let(:email) { 'michael_scott#physician.com' }
context 'when email exists in SAMPLE' do
before do
stub_request(:get, %r{/api/v1/mm_recommender})
.to_return(status: 200,
body: sample_response,
headers: {})
end
it 'fetch user data' do
expect(fetch_physician).to be_present
expect(fetch_physician).to eq(JSON.parse(sample_response))
end
end
Shouldn't I use mock rather than stub? Would there be any benefit to this? if so, what should such a test look like?
[EDIT]
context 'when email does not exist' do
let(:email) { 'test#test.com' }
let(:sample_response) { 'Not found'.to_json }
before do
stub_request(:get, %r{/api/v1/mm_recommender})
.to_return(status: 404,
body: sample_response,
headers: {})
allow(Rails.logger).to receive(:error)
end
it 'does not retrieve data' do
expect(fetch_physician).to eq('')
expect(Rails.logger).to have_received(:error).with('Request failed with code: 404, message: ')
end
end

How to skip authentication when testing rails controllers in rspec?

I have a rails backend api application integrated with auth0 service that only verifies validity of auth_token received from frontend application. After securing all backend api endpoints all my tests fail with a result "Not Authenticated", which is how it should be. However I cannot figure out how to get through the authentication and to not require it for rspec tests. Here are my classes:
projects_controller_spec.rb
require "rails_helper"
RSpec.describe Api::V1::ProjectsController, :type => :controller do
describe 'GET /api/v1/organizations/1/projects' do
let!(:organization) { create(:organization_with_projects) }
before { get :index, params: { organization_id: organization } }
context 'when authorized' do
it 'should return JSON objects' do
expect(json['projects'].count).to equal(3)
end
it { expect(response).to have_http_status(:ok) }
it { expect(response.content_type).to include('application/json') }
end
describe 'POST /api/v1/organizations/1/projects' do
let!(:organization) { create(:organization) }
let(:project) { organization.projects.first }
before { post :create, params: { organization_id: organization, project: attributes_for(:project) } }
context 'when authorized' do
it { expect(response).to have_http_status(:created) }
it { expect(response.content_type).to include("application/json") }
it { expect(json).to eq(serialized(project)) }
end
end
end
application_controller.rb
class ApplicationController < ActionController::API
include Pundit
include Secured
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
private
def record_not_found(error)
render json: { error: error.message }, status: :not_found
end
end
concerns/secured.rb
module Secured
extend ActiveSupport::Concern
included do
before_action :authenticate_request!
end
private
def authenticate_request!
# Create user if not existing
pundit_user
auth_token
rescue JWT::VerificationError, JWT::DecodeError
render json: { errors: ['Not Authenticated'] }, status: :unauthorized
end
def http_token
if request.headers['Authorization'].present?
request.headers['Authorization'].split(' ').last
end
end
def auth_token
JsonWebToken.verify(http_token)
end
def pundit_user
User.create_from_token_payload({token: auth_token[0], organization_id:
request.parameters['organization_id']})
end
end
lib/json_web_token.rb
require 'net/http'
require 'uri'
class JsonWebToken
def self.verify(token)
JWT.decode(token, nil,
true, # Verify the signature of this token
algorithm: 'RS256',
iss: 'https://xxx.auth0.com/',
verify_iss: true,
aud: Rails.application.secrets.auth0_api_audience,
verify_aud: true) do |header|
jwks_hash[header['kid']]
end
end
def self.jwks_hash
jwks_raw = Net::HTTP.get URI("https://xxx.auth0.com/.well-known/jwks.json")
jwks_keys = Array(JSON.parse(jwks_raw)['keys'])
Hash[
jwks_keys
.map do |k|
[
k['kid'],
OpenSSL::X509::Certificate.new(
Base64.decode64(k['x5c'].first)
).public_key
]
end
]
end
end
It looks like I found a solution by adding the following line into every controller spec file:
before { allow(controller).to receive(:authenticate_request!).and_return(true) }

Undefined method index? pundit testing in Rails

I am using Pundit for authorization in my application with rspec for testing.
require 'rails_helper'
describe SubjectPolicy do
subject { described_class.new(user, subject) }
let(:subject) { Subject.create }
context 'is an administrator' do
let(:role) { Role.create(role_name: 'admin') }
let(:user) { User.create(role_id: role.id) }
it { is_expected.to permit_actions([:index]) }
end
context 'is a teacher' do
let(:role) { Role.create(role_name: 'teacher') }
let(:user) { User.create(role_id: role.id) }
it { is_expected.to forbid_actions([:index]) }
end
end
When running the test for this spec test I receive the following error.
Failure/Error: it { is_expected.to permit_actions([:index]) }
NoMethodError: undefined method 'index?' for #<Subject:0x007fdcc1f70fd0>
There is a route for this index action and it is in my subjects_controller.
The code in the subject policy is very simple.
class SubjectPolicy < ApplicationPolicy
def index?
#user.is_admin?
end
end
Here is the index action in my subjects_controller
def index
#subjects = Subject.all
authorize #subjects
end
I am able to create subjects as an admin, and it does in fact block non-admins from accessing the index. But I am confused as to why this test would fail. I have this policy spec set up just like others and they are passing just fine. Any idea?

How to do testing in rails using rspec with factory girl?

Hi i am working on rails application with ruby 2.5.0 and rails 5. I have written an api to check user exist with provided username and token.
check_token_controller.rb
class CheckTokenController < ApplicationController
def create
begin
user = User.where(email: check_params[:username], token: check_params[:token]).first
if user.blank?
render json: {},
status: 401
else
render json: {},
status: 200
end
rescue => e
render json: {},
status: 500
end
end
private
def check_params
permitted = %i[username token]
params.require(:data)
.require(:attributes)
.permit(permitted)
.transform_keys(&:underscore)
end
end
now i want to test this api in my spec.rb file.
*spec/controllers/check_token_controller_spec.rb
require 'rails_helper'
describe CheckTokenController do
let(:user) { instance_double('user') }
let(:save_result) { true }
let(:params) do
{ data: { attributes: { fullname: 'michael febrianto',email: 'saddam#gmail.com', token: 'rWCyRUgfLODuc8B4DvA_8w',password: 'password' } } }
end
before do
allow(User).to receive(:new).and_return(user)
allow(user).to receive(:save).and_return(save_result)
end
let(:params) do
{ data: { attributes: { username: 'saddam#gmail.com', token: 'rWCyRUgfLODuc8B4DvA_8w' } } }
end
describe 'POST create' do
subject { post :create, params: params }
context 'when success' do
it { is_expected.to have_http_status(200) }
end
context 'when failed' do
it { is_expected.to have_http_status(401) }
end
end
end
i am first time working with rspec now whenever i run this test it doesnot create any test data i checked with debugger. please help me how can i create a test data and then i can test my api. Thanks in advance.
Please go through the books:
Everyday Rails Testing with RSpec
The RSpec Book: Behaviour-Driven Development
They might help you.

Resources