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

What is this?

portrait of Justin

This is the online home of Justin French, a designer & web application developer located in Melbourne, Australia. I like finding ways to make things work better. I like clarifying and simplifying. I like to understand how you understand things.

» read more

Subscribe to my feed

Follow me on Twitter

@justinfrench

More Notebook Articles

Show more notebook articles

Search