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
      "bar"
    end
  end

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

    its(:foo) { should eq bar }
  end


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

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).

HTH

Ash

-- 
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashleymoran

_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to