[rspec-users] Module & Mixin testing strategy

Ashley Moran ashley.moran at patchspace.co.uk
Sat Aug 14 10:26:25 EDT 2010

On 14 Aug 2010, at 11:34, Mike Howson wrote:

> Just wondered what people thoughts are to testing module's to be
> included in mixin's? Seems to me there are two main approaches:-

Hi Mike

I've been doing a lot of this sort of coding lately, as I've been extracting duplicated code into a mini-framework based on modules.

> 1. Test the behavior in a mixin object that includes the module because
> its the behavior of the object thats important not the code structure.
> 2. Test the module in isolation as it potentially code be included
> anywhere.

I'm not sure I know how option 2 is even possible, unless your module is all module methods, as you can't call instance methods on a module directly.

However, it's easy to do this in RSpec with some Ruby meta-magic:

  module MyModule
    def foo

  describe MyModule do
    let(:class_with_my_module) {
      Class.new do
        include MyModule
    subject { class_with_my_module.new }

    its(:foo) { should eq bar }

> If the best approach is 2 - to test the module in isolation and the
> module uses instance variables or methods from the object its being
> mixed with then we would need to create a test object in the rspec test
> that included the module and defined the required instance variables and
> methods. Does this lead to 1 being the best approach as we are not then
> forced to mock up a mixin just to test the module?

I'm not 100% sure but I *think* the snippet above is an implementation of what you describe here.  Please correct me if I misunderstood.

> The question came about because I recently had to get an untested rails
> module under test that was included in a number of controllers and
> depended on 'request' and 'response'. I was then faced with either
> testing one of the controllers that included that module but also added
> further complexity or defining a new thin controller used solely for
> testing the module within the spec file.

In this case, you may be able to get some mileage with the above code, but using `Class.new(ActionController::Base)`.

You can test individual objects that include your module with shared examples, for example:

  module Fooable
    def foo
  class Baz
    include Fooable
    # Oops - this is overriding Fooable#foo
    def foo
  shared_examples_for "a Fooable object" do
    # Optional
    before(:each) do
      unless respond_to?(:fooable)
        raise "You must provide instance method fooable"
    it "should have a foo of 'bar'" do
      fooable.foo.should eq "bar"
  describe Baz do
    subject { Baz.new }
    it_should_behave_like "a Fooable object" do
      let(:fooable) { subject }

My recommendation at the moment is to make the shared examples work fully-integrated (ie, no mocks).  I've run into issue where shared examples rely on mocks, which I haven't solved yet (at least not in my code - it's my next TODO).

Currently I'm doing both the above.  The isolated module spec proves the module enchants objects with the correct behaviour, the shared examples double-check that you haven't broken that behaviour in concrete classes.

See also the recent thread "Evaluating shared example customisation block before shared block" from 30th July onwards (it goes on to talk about passing parameters to shared example groups, which is possible in RSpec-2 master).




More information about the rspec-users mailing list