Testing Model Concerns With RSpec

This article will show you how to test your model concerns using RSpec.


Published on:May 19, 2016

Introduction

In our previous article about concerns: Rails 4 Concerns in Active Record Models, we learned how to create a basic concern. Well, that's great and all, but how do we test our concerns? It's actually pretty easy! In this article we will show you how to write tests for your concerns using RSpec. Let's get started.

Rails Application Setup

For this tutorial we are going to create a simple example app that adds a method called published to our model. When this published method is called, it will query a published_at field and determine whether today's date and time is greater then that of the post's published at date and time. It will also sort by published_at from newest to oldest. If published_at is nil it will be considered a draft and not published.

First, let's add the rspec-rails gem to our gemfile. Open up your gemfile and add in the lines listed below.

Gemfile:

gem 'rspec-rails'

Now run a bundle install to install the gems.

Terminal Commands:

bundle install

Next, let's run rails g rspec:install to install rspec.

Terminal Commands:

rails g rspec:install

Next, let's generate our model. In a terminal, run the command listed below to create a model called Post. The Post model should have a datetime field called published_at.

Terminal Commands:

rails g model post published_at:datetime

Now run rake db:migrate to migrate the database.

Terminal Commands:

rake db:migrate

Now let's create a concern called Publishable. Create a new file called publishable.rb in your app/models/concerns folder and add in the code listed below.

app/models/concerns/publishable.rb:

module Publishable
   extend ActiveSupport::Concern

  module ClassMethods
    def published
      where("published_at < ?", Date.current).order(published_at: :desc)
    end
  end
end

Now let's modify our Post model to include the concern. Open app/models/post.rb and modify it so that it looks like the code listed below.

app/models/post.rb:

class Post < ActiveRecord::Base
  include Publishable
end

Great, now we have our example app that authenticates our users, but how do we know if it works? Let's write some tests!

Creating the Tests

To test our newly created concern, the best thing to is create what is known as a shared example in RSpec. Don't let the fancy name fool you though, shared examples are easy to create. First, create a folder in the spec folder called concerns. Then inside the spec/concerns folder, create a new file called publishable_spec.rb. Now add in the code listed below.

spec/concerns/publishable_spec.rb:

require 'rails_helper'

shared_examples "publishable" do
  let(:entity) { described_class }
  
  let(:obj1) { entity.create(published_at: Time.current - 3.days) }
  let(:obj2) { entity.create(published_at: Time.current - 2.days) }
  let(:obj3) { entity.create(published_at: Time.current + 1.days) }
  let(:obj4) { entity.create(published_at: nil) }

  it 'returns the published posts' do
    expect(entity.published).to include(obj1, obj2)
  end

  it "does not return posts that aren't published yet" do
    expect(entity.published).to_not include(obj3)
  end

  it 'does not include drafts' do
    expect(entity.published).to_not include(obj4)
  end

  it 'sorts the published posts by published date' do
    expect(entity.published).to eq([obj2, obj1])
  end
end

If you've ever seen an RSpec test for a model before, this code will seem familiar to you. First, we include the rails_helper file. Next, we use a special method called shared_examples. This method tells RSpec we wish to share this set of tests with one or more other test suites. From there on we have a pretty standard set of tests. However, to get these tests to run, we need to tell our post test suite to execute them. To do this, open up your spec/models/post_spec.rb file and modify it so that it looks like the code listed below.

spec/models/post_spec.rb:

require 'rails_helper'

RSpec.describe Post, type: :model do
  it_behaves_like 'publishable'
end

The line that says it_behaves_like 'publishable' tells RSpec that we wish to use our publishable shared example with this model test suite. Now if you run bundle exec rspec you'll see that 4 tests were run. If you added a second model at this point and included the publishable concern on that, you'd see that the tests would also be run for that model resulting in a total of 8 tests being run, etc.

That's it! That's all there is to it! Thanks for reading!