DEV Community

Prasanna Natarajan
Prasanna Natarajan

Posted on

Rails testing tip: Remember to revert your in-memory configuration setting changes in each testcase

In Rails, each test case is wrapped in a database transaction. With both minitest and rspec. Once the test case is finished running, the transaction rolls back all the database-related changes. This means all of our app's testcases run with the exact same snapshot of the database. This ensures the database doesn't introduce any inconsistencies.

But what about the configuration settings that our app relies upon? We might write testcases that explicitly test the app's behavior due to the changes in one of these configuration settings. So if we change a value in one testcase, then it is your responsibility to restore it at the end of the testcase to it's previous value. The database framework can't help!

If you don't explicitly revert these config settings, then they'll come back to bite you later in the form of some testcase failure. It will perplex you. It did me.

So what's the solution?

I came across a good pattern in the Devise gem's source code. The idea is simple. They make the config setting change in a test helper method and yield the testing code to this helper method. One the block is run, the config setting is reverted to its old value (which was saved in the beginning). The code can explain better.

  # Execute the block setting the given values and restoring old values after
  # the block is executed.
  def swap(object, new_values)
    old_values = {}
    new_values.each do |key, value|
      old_values[key] = object.send key
      object.send :"#{key}=", value
    end
    yield
  ensure
    old_values.each do |key, value|
      object.send :"#{key}=", value
    end
  end

And used like this:

  test 'with sign_out_all_scopes as false' do
    swap Devise, sign_out_all_scopes: false do
      # test code
    end
  end

What happens here? The default value for Devise.sign_out_all_scopes is true. But in this testcase, we need to test a scenario with this value set to false. The swap method takes care of saving the current value of this attribute before changing it to the new value we want it to hold. And then it yields a block which is where we can write our test code. Once it is run, the swap method restores the saved old value to the attribute in the ensure block.

This can apply to any kind of in-memory configuration object that allows both reading and writing. Make use of this tactic to avoid future headaches!

Discussion (0)