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

Ashley Moran
Mon, 26 Jul 2010 12:09:44 -0000

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 "" 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/