Testing modules in RSpec

Viewed 78830

What are the best practices on testing modules in RSpec? I have some modules that get included in few models and for now I simply have duplicate tests for each model (with few differences). Is there a way to DRY it up?

14 Answers

To test your module, use:

describe MyCoolModule do
  subject(:my_instance) { Class.new.extend(described_class) }

  # examples
end

To DRY up some things you use across multiple specs, you can use a shared context:

RSpec.shared_context 'some shared context' do
  let(:reused_thing)       { create :the_thing }
  let(:reused_other_thing) { create :the_thing }

  shared_examples_for 'the stuff' do
    it { ... }
    it { ... }
  end
end
require 'some_shared_context'

describe MyCoolClass do
  include_context 'some shared context'

  it_behaves_like 'the stuff'

  it_behaves_like 'the stuff' do
    let(:reused_thing) { create :overrides_the_thing_in_shared_context }
  end
end

Resources:

One possible solution for testing module method which are independent on class that will include them

module moduleToTest
  def method_to_test
    'value'
  end
end

And spec for it

describe moduleToTest do
  let(:dummy_class) { Class.new { include moduleToTest } }
  let(:subject) { dummy_class.new }

  describe '#method_to_test' do
    it 'returns value' do
      expect(subject.method_to_test).to eq('value')
    end
  end
end

And if you want to DRY test them, then shared_examples is good approach

This is a recurrent pattern since you're going to need to test more than one module. For that reason, this is more than desirable to create a helper for this.

I found this post that explains how to do it but I'm coping here since the site might be taken down at some point.

This is to avoid the object instances do not implement the instance method: :whatever error you get when trying to allow methods on dummy class.

Code:

In spec/support/helpers/dummy_class_helpers.rb

module DummyClassHelpers

  def dummy_class(name, &block)
    let(name.to_s.underscore) do
      klass = Class.new(&block)

      self.class.const_set name.to_s.classify, klass
    end
  end

end

In spec/spec_helper.rb

# skip this if you want to manually require
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}

RSpec.configure do |config|
  config.extend DummyClassHelpers
end

In your specs:

require 'spec_helper'

RSpec.shared_examples "JsonSerializerConcern" do

  dummy_class(:dummy)

  dummy_class(:dummy_serializer) do
     def self.represent(object)
     end
   end

  describe "#serialize_collection" do
    it "wraps a record in a serializer" do
      expect(dummy_serializer).to receive(:represent).with(an_instance_of(dummy)).exactly(3).times

      subject.serialize_collection [dummy.new, dummy.new, dummy.new]
    end
  end
end
Related