Testing Active Record Concerns with RSpec and Temping

Published on August 23, 2014 · 3 mins

I won’t even attempt to discuss the utility of concerns. To some people, they’re a dangerous way of hiding dependency and just syntactic sugar for include/extend. To others, they should be used whenever possible. To me, concerns are just a tool. I try not to use them too much, but I don’t freak out when I see them either.

Instead, I’m going to focus on the practical side of the matter: what is the best approach to testing your concerns?

Let’s assume you have the following Processable concern:

require 'active_support/concern'

module Processable
  extend ActiveSupport::Concern

  included do
    scope :processed, ->{ where processed: true }
    scope :unprocessed, ->{ where processed: false }
  end

  module ClassMethods
    # nothing here
  end

  def mark_as_processed!
    update_column :processed, true
  end
  
  def mark_as_unprocessed!
    update_column :processed, false
  end
  
  def status
    if processed?
      :processed
    else
      :unprocessed
    end
  end
  
  def unprocessed?
    !processed?
  end
end

And you have a generic Task model that includes it:

class Task < ActiveRecord::Base
  include Processable
end

The most obvious approach would be to test the concern’s methods directly on the model. But what if you include the concern in more than one model? (That’s what concerns are for, after all!) Well, you can go two ways from here:

  • you can use a shared examples group, or
  • you can test the concern on an ideal, disposable model.

Since there are plenty of articles out there on RSpec shared examples, I will focus solely on the second approach.

What we’re going to do is create a model that has only the columns used by the concern. The model will then include that concern, allowing us to test instance and class methods.

However, we don’t want to create an actual model just for testing purposes, do we? That’s where the Temping gem comes in handy.

Creating temporary models with Temping

Let’s get down to business, then. First of all, you’ll have to install the gem by adding it to your Gemfile (you can put it in the test group).

Once it’s done, you can create your test like this:

require 'rails_helper'

RSpec.describe Processable do
  before(:all) do
    Temping.create :processable_model do
      include Processable

      with_columns do |t|
        t.boolean :processable, default: false
      end
    end
  end

  subject { ProcessableModel.new }
  
  # ...
end

What we’re doing, here, is tell Temping to create a temporary model named ProcessableModel with the processable boolean column. This model includes the Processable concern.

After the model has been set up, you can do whatever you want: Create new instances, call class and instance methods and pretty much anything else you would do with an Active Record model.

For example, let’s test the mark_as_processed! method:

require 'rails_helper'

RSpec.describe Processable do
  # ...
  
  describe '#mark_as_processed!' do
    it 'updates the processed column to true' do
      subject
        .expects(:update_column)
        .with(:processed, true)
        .once
        .returns(true)

      subject.mark_as_processed!
    end
  end
  
  # ...
end

Cool, right? It’s faster than RSpec shared examples (because you test your concern exactly once, not once for each model that includes it) and extremely flexible.

Alright, that’s it. Testing the concern’s scopes and the other methods is up to you. Have fun!

See you soon?
© 2025 Alessandro Desantis