Search code examples
rubyrspecglobal-variables

Set variable once for all examples in RSpec suite (without using a global variable)


What is the conventional way to set a variable once to be used by all examples in an RSpec suite?

I currently set a global variable in spec_helper that checks whether the specs are being run in a "debug mode"

$debug = ENV.key?('DEBUG') && (ENV['DEBUG'].casecmp('false') != 0) && (ENV['DEBUG'].casecmp('no') != 0)

How do I make this information available to all the examples in the suite without using a global variable and without re-computing the value for each context and/or example? (My understanding is that using a before(:all) block would re-compute it once per context; but, that before(:suite) can't be used to set instance variables.)

(Note: I'm asking more to learn about good design that to address this specific problem. I know one global isn't a big deal.)


Solution

  • for this purpose I usually write custom modules that I can include in the spec_helper.rb file.

    Let's say that I am testing a back-end API and I don't want to parse every time the JSON response body.

    spec/
    spec/spec_helper.rb
    spec/support/request_helper.rb
    spec/controllers/api/v1/users_controller_spec.rb
    

    I first define a function in a module placed under the support subfolder.

    # request_helper.rb
    module Request
      module JsonHelpers
        def json_response
          @json_response ||= JSON.parse(response.body, symbolize_names: true)
        end
      end
    end
    

    Then I include this module by default for some test types

    #spec_helper.rb
    #...
    RSpec.configure do |config|
      config.include Request::JsonHelpers, type: :controller
    end
    

    Then I use methods defined in the test.

    # users_controller_spec.rb
    require 'rails_helper'
    
    RSpec.describe Api::V1::UsersController, type: :controller do
      # ...
    
     describe "POST #create" do
    
        context "when is successfully created" do
          before(:each) do
            @user_attributes = FactoryGirl.attributes_for :user
            post :create, params: { user: @user_attributes }
          end
    
          it "renders the json representation for the user record just created" do
            user_response = json_response[:user]
            expect(user_response[:email]).to eq(@user_attributes[:email])
          end
    
          it { should respond_with 201 }
        end
    end
    

    In your case, you could create a module such as

    module EnvHelper
      def is_debug_mode?
        @debug_mode ||= ENV['DEBUG']
      end
    end
    

    Then you can include it and simply use the method is_debug_mode? in your tests.