Testing Rails Plugin Config Preferences

Published 29 January 10 by Justin French

Time to start blogging again, I hope! Most plugins and Ruby gems have configurable preferences, and usually they’re implemented as a Module or Class attribute, something like this:

class AwesomeGem
  cattr_accessor :something
  @@something = true
end

In the case of a Rails plugin or gem, these are then modified with an initializer as Rails boots up:

AwesomeGem.something = false

Despite my best efforts to be opinionated, Formtastic has about 15 preferences today, and I can see another 10 or so on the horizon. They all need to be tested. For a while there, we had some pretty brittle tests that did this sort of stuff:

describe AwesomeGem, "something preference" do
  describe "when true" do
    it "should..." do
      AwesomeGem.something = true
      # ...
    end
  end
  describe "when false" do
    it "should..." do
      AwesomeGem.something = false
      # ...
    end
  end
end

The problem here is that we’ve changed AwesomeGem.something’s configuration from true (the default in the class) to false. Any tests that assume the default preference will fail. Maybe. Who knows. Depends on the order your tests run.

So we need to return the preference to it’s default state at the end of any tests that alter it:

it "should..." do
  AwesomeGem.something = false
  # ...
  AwesomeGem.something = true
end

This is stupid and short-sighted. Next week, someone changes the default value, and everything breaks. What you need to do is capture the value before you change it, then return it to that state at the end of the test:

it "should..." do
  old_value = AwesomeGem.something
  AwesomeGem.something = false
  # ...
  AwesomeGem.something = old_value
end

This works, but it’s shit code, especially if you’re repeating it in dozens of specs. You can DRY it up a bit by moving it to before and after blocks, but it’s a heap of noisy, ugly code. We can do better. How about wrapping the above pattern up in a block that temporarily changes the configuration, like this:

it "should..." do
  with_config :something, false do
    # ...
  end
end

Here’s the implementation, throw it in your spec_helper.rb file:

def with_config(preference_name, temporary_value, &block)
  old_value = AwesomeGem.send(preference_name)
  AwesomeGem.send(:"#{preference_name}=", temporary_value)
  yield
  AwesomeGem.send(:"#{preference_name}=", old_value)
end

Enjoy!

Options

Search