1
votes

I am trying to stub out an :authenticate_user method call in my request spec so I can test the user's association creation. I am using these blog posts as a guide on stubbing:

1) https://8thlight.com/blog/mike-knepper/2014/07/01/stubbing-authentication-and-authorization-in-controller-specs.html

2) http://johnnyji.me/rspec/2015/06/18/stubbing-controller-instance-methods-in-rspec.html

I'm not having any success with stubbing and I can't figure out what am I missing.

When I tried

it 'creates a new contract' do
      allow(controller).to receive(:authenticate_user).and_return(user)
      post api_v1_user_contracts_path(user), { params: contract_params}
      expect(response).to have_http_status(200)
    end

I got: enter image description here

When I tried:

it 'creates a new contract' do
      allow_any_instance_of(controller).to receive(:authenticate_user).and_return(user)
      post api_v1_user_contracts_path(user), { params: contract_params}
      expect(response).to have_http_status(200)
    end

I got enter image description here

My code: spec/requests/contracts_api_spec.rb

require 'rails_helper'
require 'pry'
context "POST #create" do
    let (:user) { User.create(full_name: "Jason Bourne", email: "[email protected]", password: "123456") }
    let (:contract_params) do
      {
        "contract[vendor]" => "Lebara",
        "contract[starts_on]" => "2018-12-12",
        "contract[ends_on]" => "2018-12-16",
        "contract[price]" => "15"
      }
    end

    it 'creates a new contract' do
      allow(controller).to receive(:authenticate_user).and_return(user)

      post api_v1_user_contracts_path(user), { params: contract_params}
      expect(response).to have_http_status(200)
    end

app/controllers/api/v1/contracts_controller.rb

class Api::V1::ContractsController < ApplicationController
  before_action :authenticate_user

  def show
    if @current_user.contracts.find_by(id: params[:id])
      render json: @current_user.contracts.find_by(id: params[:id])
    else
      render json: { error: "Contract not found"}, status: 400
    end
  end

  def create
    contract = @current_user.contracts.build(contract_params)
    if contract.save
      render json: contract
    else
      render json: { error: contract.errors }, status: 400
    end
  end

app/controllers/concerns/token_authenticatable.rb

class NotAuthorizedException < StandardError; end

module TokenAuthenticatable
  extend ActiveSupport::Concern

  included do
    attr_reader :current_user

    before_action :authenticate_user

    rescue_from NotAuthorizedException, with: -> { render json: { error: 'Not Authorized' }, status: :unauthorized }
  end

  private

  def authenticate_user
    @current_user = DecodeAuthenticationCommand.call(request.headers).result
    raise NotAuthorizedException unless @current_user
  end
end

Additional questions:

1) Should I be using a real User object, or should that be a double? I'm assuming it should be a real user in order to test if the association creation is working.

2) Should I be using allow(Api::V1::ContractsController).to receive(:authenticate_user).and_return(user)? I've tried it before and didn't work but I didn't know it was because something else also was breaking it.

Thanks for any feedback you can give!

1
I just tried and it works, thanks :). I guess I couldn't use a test double for the user or else there would be no point to this test?MLZ
I have moved my comment to the answer, it will be better for others.MrShemek

1 Answers

2
votes

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.