---
title: Inject that Rails Configuration Dependency!
teaser:
tags: web,rails,testing,good code
author: Josh Clayton
published_on: 2012-01-25
---

Setting up configuration settings in a Rails app has been fairly straightforward
for a while now:

    # config/environments/development.rb
    Doit::Application.configure do
      config.default_creator = "Person 1"
    end

    # config/environments/test.rb
    Doit::Application.configure do
      config.default_creator = "Test Person"
    end

    # config/environments/production.rb
    Doit::Application.configure do
      config.default_creator = "John Doe"
    end

To access this setting, call `Doit::Application.config.default_creator` within
your app. Pretty straightforward, right?

    class TodoItem < ActiveRecord::Base
      before_create :assign_default_creator, unless: :creator?

      private

      def assign_default_creator
        self.creator = Doit::Application.config.default_creator
      end
    end

Let's imagine you have a todo item and you want to test that the value gets
assigned if no creator is provided.

Imagine how you'd test this.

    describe TodoItem do
      it "assigns the default creator when no creator is assigned" do
        Doit::Application.config.stub(:default_creator).and_return("default creator from config")
        subject.save
        subject.creator.should == "default creator from config"
      end

      it "does not assign the default creator if it has been set" do
        Doit::Application.config.stub(:default_creator).and_return("default creator from config")
        subject.creator = "Jane Doe"
        subject.save
        subject.creator.should == "Jane Doe"
      end
    end

There's a few things that are gross. First, we're referencing
`Doit::Application` in the model spec. Second, we're stubbing in both examples.
The latter is a low-hanging fruit so it can be extracted.

    describe TodoItem do
      it "assigns the default creator when no creator is assigned" do
        config_default_creator_returns("default creator from config")
        subject.save
        subject.creator.should == "default creator from config"
      end

      it "does not assign the default creator if it has been set" do
        config_default_creator_returns("default creator from config")
        subject.creator = "Jane Doe"
        subject.save
        subject.creator.should == "Jane Doe"
      end

      def config_default_creator_returns(value)
        Doit::Application.config.stub(:default_creator).and_return(value)
      end
    end

Now, interacting with `Doit::Application` is confined to one method. Some people
may stub the application in an RSpec `before` block, but I don't like doing that
because one of the specs cares about the stubbed value. I want that stub right
in the example so it's obvious that the stub and assertion are close (in number
of lines).

Even though `Doit::Application` is confined to one call in the spec, I really
don't like that the spec cares about its `config` at all. What I'd love to do is
assign a custom configuration on my `TodoItem` in my test intsead of caring
about `Doit::Application` and having to stub on `config`. I can do this with
dependency injection.

Right now, `TodoItem` has a dependency on `Doit::Application.config`. Dependency
injection would mean `TodoItem` gets a `class_attribute :config` that defaults
to `Doit::Application.config` but can be overridden (say, in our tests).

    describe TodoItem do
      it "assigns the default creator when no creator is assigned" do
        subject.config = stub("config", default_creator: "default creator from config")
        subject.save
        subject.creator.should == "default creator from config"
      end

      it "does not assign the default creator if it has been set" do
        subject.config = stub("config", default_creator: "default creator from config")
        subject.creator = "Jane Doe"
        subject.save
        subject.creator.should == "Jane Doe"
      end
    end

With the class attribute, I'm able to override config on the instance and
replace it with a stub that has a `default_creator` method, which I've assigned
to the string I expect. I was able to remove my reference of `Doit::Application`
from the spec. Perfect!

Here's the model code:

    class TodoItem < ActiveRecord::Base
      class_attribute :config
      self.config = Doit::Application.config

      before_create :assign_default_creator, unless: :creator?

      private

      def assign_default_creator
        self.creator = config.default_creator
      end
    end

The callback `assign_default_creator` now doesn't care about
`Doit::Application.config`, only that `config` has a `default_creator` method.

This post actually stemmed from my interaction with a developer in [Factory Bot
GitHub Issues](https://github.com/thoughtbot/factory_bot/issues/269) who asked
a pretty interesting question about reloading classes in Factory Bot after
removing the constant and loading the Ruby file again (it seemed like a code
smell and not an issue with Factory Bot).

At the end of the thread, I suggested he attend my [Test-Driven Rails
workshop](https://thoughtbot.com/upcase/test-driven-rails) next week, January 30th and
31st, because I'll be talking about RSpec and dependency injection (among other
things like Cucumber, how, when, and what to test in a Rails app). It's perfect
for Rails developers who are interested in writing more cleaner, more stable
applications.

See you there!
