[rspec-users] Formatting shared example descriptions with test data

Ashley Moran ashley.moran at patchspace.co.uk
Mon Jul 26 08:09:44 EDT 2010


On Jul 26, 2010, at 8:55 am, Wincent Colaiuta wrote:

> Seems to me that including the same shared example group twice in the same "describe" block is a bit of an abuse, to be honest. I don't think it was ever really intended to be used in that way.

You're right, it clearly wasn't intended for this.  I'm trying to find the best way to express the behaviour I want without bending the current syntax of RSpec too much.  This is indeed a toy example, so let me explain the real situation in more detail.

I'm doing a small side project to make a checklist app.  As part of that, I'm trying to extract out a library similar to Naked Objects[1].  One of the things that can be factored out is collections inside entities.  So I currently have, as examples:

  class User
    extend DomainLib::Entity::ClassExtensions
    include DomainLib::Entity::InstanceExtensions

    collection :checklist_templates, of: :checklist_template, class_name: "ChecklistTemplate"
  end

and

  class ChecklistTemplate
    extend DomainLib::Entity::ClassExtensions
    include DomainLib::Entity::InstanceExtensions
  
    collection :items, of: :item, class_name: "ChecklistTemplateItem"
  end

Now one of the thing that bugs me about using ORM (eg ActiveRecord, DataMapper) features for this is you're then faced with the dilemma of do you do an integration test of the collection functionality, which duplicates a lot of the testing effort put into the ORM, or do you mock this out, and risk having false positives because the ORM behaves differently than your test setup assumes?

The solution I'm playing with is to extract shared contract (ie shared example groups) that you can mix into a spec for a host class (eg User, Checklist) above to prove the feature (here collections) works, without reference to the implementation.  (The specs inside DomainLib prove the general case.)

With the help of this spec_helper incantation:

  module SpecHelperObjectExtensions
    def contract(name, &block)
      shared_examples_for(name, &block)
    end
  end
  include SpecHelperObjectExtensions

  RSpec.configure do |c|
    c.alias_it_should_behave_like_to(:it_satisfies_contract, 'satisfies contract:')
  end

I've already been able to extracted contract, which is for Representation (basically, a view object that isn't much more than a Struct):

  # Params:
  # * representation_class
  # * properties
  contract "Representation" do
    # ...
    # Setup and other examples omitted
    # ...
    
    describe "#==" do
      it "is true for Representations with the equal attributes" do
        representation_class.new(default_attributes).should eq representation_class.new(default_attributes)
      end
    
      it "is false if any property is different" do
        properties.each do |property|
          representation_class.new(default_attributes).should_not eq(
            representation_class.new(default_attributes_with_different_value_for(property))
          )
        end
      end
    end

  end

This is fine for a class, but the behaviour I want to prove with a Collection needs to be mixed in once per collection, eg (the last two are made up):

  describe User do
    it_satisfies_contract "Entity Collection", for: "checklist_templates"
    it_satisfies_contract "Entity Collection", for: "groups"
    it_satisfies_contract "Entity Collection", for: "delegated_actions"
  end
  
>  describe Integer do
>    [1, 2].each do |i|
>      describe i do
>        it_should_behave_like 'Comparable'
>      end
>    end
>  end
> 
> ...
> 
> I know you probably have some real example in mind hiding behind that toy example, but I believe anything you want to test can be written in the same way (ie. without needing to inject the "<i>" into your shared examples).

So as you can see, in the real (non-toy) example there's no object to be described: it's an aspect of behaviour of the test subject, and one than can occur N times.  I'm aware that I'm twisting RSpec quite a bit to try to achieve this.

If you (or anyone) have any thoughts though, I'd love to hear them.  This one is messing with my head a bit :)

Cheers
Ash


[1] http://www.nakedobjects.org/


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



More information about the rspec-users mailing list