FelixFortis
FelixFortis

Reputation: 724

Rspec - How can I stub a constant that is defined in config/environment.rb?

I have defined a constant in config/environment.rb called LOCAL_SETTINGS that I use for configuration and use throughout my app. It's a YAML file stored in config/local_settings.yml and will sometimes contain sensitive data like API keys etc.

I'm currently trying to write a spec for a method that uses LOCAL_SETTINGS["slack"]["slack_token"].

The problem is that the constant is not being stubbed for my expectations. ie expect(subject.path).to eq(help_request) fails because it returns a path including the non-stubbed LOCAL_SETTINGS hash.

However if I put a debugger in beneath stub_const and then type LOCAL_SETTINGS, I can see that stub_const has worked.

My questions are:

  1. Is there something I can do in Rspec to get stubs working for constants defined in config/environment.rb?
  2. Should I simply define this constant elsewhere? If so, where? I will need access to it throughout my application in the app/ lib/ and spec/ folders.

My config/environment.rb file:

# Load the Rails application.
require_relative 'application'

LOCAL_SETTINGS = YAML.load_file("#{Rails.root}/config/local_settings.yml")

# Initialize the Rails application.
Rails.application.initialize!

My spec:

describe RequestBuilder, type: :model do
  let(:help_params) { {"user_name"=>"some_user_name", "text"=>"release-bot help"} }
  let(:help_builder) { RequestBuilder.new(help_params) }
  let(:help_request) { "/api/files.upload?file=lib%2Fresponses%2Fhelp&filetype=ruby&channels=stubbed_channel&token=stubbed_token" }
  let(:slack_settings) { {"slack"=>{"slack_token"=>"stubbed_token", "slack_channel"=>"stubbed_channel"}} }

  context 'Given an incoming request' do
    context 'With a correctly formatted request' do
      context 'And the "help" command' do

        subject { help_builder.build_request_hash }

        it 'builds a request containing the help data' do
          stub_const("LOCAL_SETTINGS", slack_settings)

          expect(subject).to have_key(:request)
          expect(subject[:request].path).to eq(help_request)
        end
      end
    end
  end
end

Upvotes: 3

Views: 1748

Answers (1)

Adam Sheehan
Adam Sheehan

Reputation: 2172

If you're using Rails, and you already have a .yaml file in the config directory, I would suggest looking at Rails custom configuration to load the YAML file. This way you'll be able to isolate any credentials from your test environment without having to stub the LOCAL_SETTINGS const for every test or change any of your classes.

# config/local_settings.yml
development:
    slack_token: some_dev_token
    slack_channel: some_channel

test:
    slack_token: fake_token
    slack_channel: fake_channel

production:
    slack_token: <%= ENV['SLACK_TOKEN'] %>
    slack_channel: <%= ENV['SLACK_CHANNEL'] %>

And then to load this configuration file:

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.local_settings = config_for(:local_settings)
  end
end

Then you can access the values from Rails.configuration.local_settings['slack_token'] rather than from the LOCAL_SETTINGS constant.

Another blog post highlighting the custom configuration.

Upvotes: 2

Related Questions