Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can I change environment variables when running rspec for ruby?

I have several ruby scripts and test them with rspec.

I put my environments in a env.rb file (for now) so I can access them locally and in production put them in the config variables.

But when I run rspec, I would like different environment variables. Two use cases:

  • I run Twilio, so I want to be able to change the SID used to their Test Credentials
  • I store things in a database as a service, and want to have a separate test database
like image 715
Satchel Avatar asked Dec 19 '14 06:12

Satchel


2 Answers

You can

  • set ENV vars explicitly in the context with ENV["FOO_BAR"] = "baz"
  • check Rails.env.test? in your initializers to setup twilio and other clients with test specific opts
  • have a file with all your env setup for test and then use dotenv

I personally prefer to use ENV vars only when creating objects and then passing the env var value to the constructor, so I can test the object class without caring about ENV, and I can test the object that initializes the other object with the env var, by just asserting the creation used the env var.

So you'd change something like

class Client
  def initialize
    @restclient = RestClient::Resource.new(ENV["API_URL"])
  end
end

to

class Client
  def initialize(url)
    @restclient = RestClient::Resource.new(url)
  end
end

and have whatever is initializing that instance to then pass the value of the env var

def fetch_content
  client = Client.new(ENV["API_URL"])
  # ...
end

this way you can test the Client class without caring about the env var by just passing any url, and then can test the class that instantiates the client as

it "uses client" do
  ENV["API_URL"] = "foo.com"
  expect(Client).to receive(:new).with(ENV["API_URL"])  
  subject.fetch_content
end

one problem with updating the env var is that the change persist throughout the rest of the test suite, and may cause problems if you don't expect it to be there in some tests, in these cases you can mock the value with

expect(ENV).to receive(:[]).with("API_URL").and_return("foo.com")
like image 117
rafb3 Avatar answered Oct 13 '22 02:10

rafb3


Since ENV is global state you want to reset the value after each spec to avoid it leaking between specs:

describe 'enabled?' do
  around do |example|
    env_value_before = ENV['MIXPANEL_ENABLED']
    ENV['MIXPANEL_ENABLED'] = env_value
    example.run
    ENV['MIXPANEL_ENABLED'] = env_value_before
  end

  context 'when ENV not set' do
    let(:env_value) { nil }

    it 'returns true' do
      expect(subject.enabled?).to eq(true)
    end
  end

  context 'when ENV is set' do
    let(:env_value) { '1' }

    it 'returns false' do
      expect(subject.enabled?).to eq(false)
    end
  end
end
like image 28
Kris Avatar answered Oct 13 '22 03:10

Kris