Search code examples
ruby-on-railsrspecrspec-railsrspec-mocks

Rspec stubbing a constant set to a Rails credential


I have a class:

class Vendor::Connection
  VENDOR_CLIENT_ID = Rails.application.credentials.vendor_api[:client_id].freeze
  VENDOR_CLIENT_SECRET = Rails.application.credentials.vendor_api[:client_secret].freeze

  # ...

  def token_body
    {
      client_id: VENDOR_CLIENT_ID,
      client_secret: VENDOR_CLIENT_SECRET,
    }
  end
end

When I attempt to mock the credentials using any of the methods in this Rspec Github Issue

require 'rails_helper'

describe Vendor::Connection do
  before do
    allow(Rails.application.credentials).to receive(:vendor_api).and_return({ client_id: '123' })
  end

  it 'works' do
    expect(true).to eq(true)
  end
end

I receive:

Failure/Error: VENDOR_CLIENT_ID = Rails.application.credentials.vendor_api[:client_id]

NoMethodError:
  undefined method `[]' for nil:NilClass

I also tried a before block of:

  before do
    stub_const('Vendor::Connection::VENDOR_CLIENT_ID', '123')
    stub_const('Vendor::Connection::VENDOR_CLIENT_SECRET', '456')
    stub_const('Vendor::Connection::VENDOR_PARTNER_ID', '789')
  end

I still receive:

Failure/Error: VENDOR_CLIENT_ID = Rails.application.credentials.vendor_api[:client_id]

NoMethodError:
  undefined method `[]' for nil:NilClass

I tried:

shared_context 'credentials' do
  before do
    allow(Rails.application).to receive(:credentials).and_return(OpenStruct.new(vendor_api: {client_id: '123', client_secret: '456', partner_id: '789'}))
  end
end

describe Vendor::Connection do
  include_context 'credentials'

  it 'works' do
    expect(true).to eq(true)
  end
end

Which also returned:

Failure/Error: VENDOR_CLIENT_ID = Rails.application.credentials.vendor_api[:client_id]

NoMethodError:
  undefined method `[]' for nil:NilClass

I also attempted to put a binding.pry as the first line in the before block, but it fails before getting to the binding.pry

Running on: ruby 3.0.5 rails 6.1.7.4 rspec 3.10.0 rspec-rails 4.1.2

Any help would be greatly appreciated


Solution

  • I suggest to use different credentials for different environments and do not stub them

    rails credentials:edit --environment test
    

    The above command does the following:

    • creates config/credentials/test.key if missing

    • creates config/credentials/test.yml.enc if missing

    • decrypts and opens test credentials file in the default editor

    Since that credentials and key are for tests only, you can commit both files with Git and use them in CI if needed