Stubbing environment variables in RSpec

To test code based on some environment variables configuration, you usually need a way to define environment variables on an individual test case level and be sure this configuration won't leak from test to test.

In this scenario, RSpec's method stubbing comes in handy:

# spec/support/env_helpers.rb
module EnvHelpers
  def stub_env(variable_name, value)
    allow(ENV).to receive(:[]).and_call_original
    allow(ENV).to receive(:[]).with(variable_name).and_return(value)
    allow(ENV).to receive(:fetch).and_call_original
    allow(ENV).to receive(:fetch).with(variable_name).and_return(value)
  end

  RSpec.configure { |config| config.include self }
end

Require this module from spec/spec_helper.rb for new helpers availability in the specs:

require_relative "./support/env_helpers"

Usage example with custom configuration file in a Rails project:

# config/external_api.yml
development:
  api_key: "non_secret_api_key"
test:
  api_key: <%= ENV["API_KEY"] %>
production:
  api_key: <%= ENV["API_KEY"] %>

The spec:

# specs/configuration/external_api_spec.rb
RSpec.describe "external api configuration" do
  subject(:configuration) { Rails.application.config_for("external_api") }

  context "with sample configuration" do
    before { stub_env("API_KEY", "test_api_key") }

    it { expect(configuration.api_key).to eq("test_api_key") }
  end

  context "with no configuration" do
    it { expect(configuration.api_key).to be_nil }
  end

  context "with direct environment variables access" do
    before { stub_env("SAMPLE", "value") }

    it { expect(ENV["SAMPLE"]).to eq("value") }
    it { expect(ENV.fetch("SAMPLE")).to eq("value") }
  end
end

References:



Somewhat related: