# Testing Code in a Rails Initializer

This post is part of our historical archive and represents the beliefs, actions, products, and services of Code Climate as of its publication date. Today, Code Climate focuses on providing enterprise leaders the software development data, context layer, and playbooks needed to build the AI-native software organization their enterprise needs. Head to codeclimate.com to learn more.

Jul 23, 2013

Rails prides itself on sane defaults, but also provides hooks for customizing the framework by providing Ruby blocks in your configuration files. Most of this code begins and ends its life simply and innocuously. Sometimes, however, it grows. Maybe it’s only 3 or 4 lines, but chances are they define important behavior.

Pretty soon, you’re going to want some tests. But while testing models and controllers is a well-established practice, how do you test code that’s tucked away in an initializer? Is there such thing as an _initializer test_?

No, not really. But that’s ok. Configuration or DSL-style code can trick us into forgetting that we have the full arsenal of Ruby and OO practices at our disposal. Let’s take a look at a common idiom found in initialization code and how we might write a test for it.

## Configuring Asset Hosts

Asset host configuration often starts as a simple String:

```ruby
config.action_controller.asset_host = "assets.example.com"
```

Eventually, as the security and performance needs of your site change, it may grow to:

```ruby
config.action_controller.asset_host = Proc.new do |*args|
  source, request = args

if request.try(:ssl?)
    'ssl.cdn.example.com'
  else
    'cdn%d.example.com' % (source.hash % 4)
  end
end
```

Rails accepts an asset host `Proc` which takes two arguments – the path to the source file and, when available, the request object – and returns a computed asset host. What we really want to test here is not the assignment of our `Proc` to a variable, but the logic inside the `Proc`. If we isolate it, it’s going to make our lives a bit easier.

Since Rails seems to want a `Proc` for the asset host, we can provide one. Instead of embedding it in an environment file, we can return one from a method inside an object:

```ruby
class AssetHosts
  def configuration
    Proc.new do |*args|
      source, request = args

if request.try(:ssl?)
        'ssl.cdn.example.com'
      else
        'cdn%d.example.com' % (source.hash % 4)
      end
    end
  end
end
```

It’s the exact same code inside the `#configuration` method, but now we have an object we can test and refactor. To use it, simply assign it to the `asset_host` config variable as before:

```ruby
config.action_controller.asset_host = AssetHosts.new.configuration
```

At this point, you may see an opportunity to leverage Ruby’s duck typing, and eliminate the explicit `Proc` entirely, instead providing an `AssetHosts#call` method directly. Let’s see how that would work:

```ruby
class AssetHosts
  def call(source, request = nil)
    if request.try(:ssl?)
      'ssl.cdn.example.com'
    else
      'cdn%d.example.com' % (source.hash % 4)
    end
  end
end
```

Since Rails just expects that the object you provide for the `asset_hosts` variable respond to the `#call` interface (like `Proc` itself does), you can simplify the configuration:

```ruby
config.action_controller.asset_host = AssetHosts.new
```

Now let's wrap some tests around `AssetHosts`. Here’s a first cut:

```ruby
describe AssetHosts do
  describe "#call" do
    let(:https_request) { double(ssl?: true) }
    let(:http_request)  { double(ssl?: false) }

context "HTTPS" do
      it "returns the SSL CDN asset host" do
        AssetHosts.new.call("image.png", https_request).
          should == "ssl.cdn.example.com"
      end
    end

context "HTTP" do
      it "balances asset hosts between 0 - 3" do
        asset_hosts = AssetHosts.new
        asset_hosts.call("foo.png", http_request).
          should == "cdn1.example.com"

asset_hosts.call("bar.png", http_request).
          should == "cdn2.example.com"
      end
    end

context "no request" do
      it "returns the non-ssl asset host" do
        AssetHosts.new.call("image.png").
          should == "cdn0.example.com"
      end
    end
  end
end
```

It’s not magic, but the beauty of first-class objects is they have room to breathe and help present refactorings. In this case, you can apply the _Composed Method_ pattern to `AssetHosts#call`.

Guided by tests, you might end up with an object that looks like this:

```ruby
class AssetHosts
  def call(source, request = nil)
    if request.try(:ssl?)
      https_asset_host
    else
      http_asset_host(source)
    end
  end

private

def http_asset_host(source)
    'cdn%d.example.com' % cdn_number(source)
  end

def https_asset_host
    'ssl.cdn.example.com'
  end

def cdn_number(source)
    source.hash % 4
  end
end
```

Since the external behavior of `AssetHosts` hasn’t changed, no changes to the tests are required.

By making a small leap – isolating configuration code into an object – we now have logic that is easier to test, read, and change. If you find yourself stuck in a similar situation, with important logic stuck in a place that resists testing, see where a similar leap can lead you.
