Formatting shared example descriptions with test data

Hi

I’m back again, and still on a quest to tame shared example to do my
bidding. This time what I’m wondering is… is there any way to format
shared example specdoc description output with data passed in with #let,
or otherwise? eg: How do I get “1” and “2” into either of the
placeholders here?

shared_examples_for “Comparable” do
describe “comparing ” do
it “defines equality for ” do
object.should eq object
end
end
end

describe Integer do
it_should_behave_like “Comparable” do
let(:object) { 1 }
end
it_should_behave_like “Comparable” do
let(:object) { 2 }
end
end

(If I had to duplicate the 1 and 2 that wouldn’t be the end of the
world, as long as the syntax for including the shared examples was
simple enough.)

Cheers
Ash


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

El 26/07/2010, a las 09:31, Ashley M.
escribió:

describe Integer do
it_should_behave_like “Comparable” do
let(:object) { 1 }
end
it_should_behave_like “Comparable” do
let(:object) { 2 }
end
end

(If I had to duplicate the 1 and 2 that wouldn’t be the end of the world, as long as the syntax for including the shared examples was simple enough.)

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.

Your shared_examples_for should be written in such a way that they don’t
need to reference “”; ie:

shared_examples_for “Comparable” do
it ‘defines equality’ do
subject.should eq(subject)
end
end

And then you use it like this:

describe Integer do
[1, 2].each do |i|
describe i do
it_should_behave_like ‘Comparable’
end
end
end

Obviously this is a toy example that you’ve given us, and I don’t see
why you’d want to test 1 and 2 like that, but for the purposes of
illustration… The specdoc output for that would look like:

Integer
1
it should behave like Comparable
defines equality
2
it should behave like Comparable
defines equality

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

Cheers,
Wincent

On Jul 26, 2010, at 8:55 am, Wincent C. 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

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 :slight_smile:

Cheers
Ash

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


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

On Jul 26, 2010, at 3:44 pm, Wincent C. wrote:

Personally I wouldn’t do this. It makes it harder for anybody coming to your project to understand what’s going on, because they see this “contract ‘foo’ do” construct and don’t know what it is unless they dig into your spec_helper. If you really want the word contract to appear in there I would just write the shared examples like this:

Actually, this doesn’t bother me. All the commercial development I do
is pair-programmed, so it doesn’t take long for anyone new to a project
to pick things like these up. I prefer to improve the semantics at the
expense of some initial learning cost. (As it happens, this isn’t a
commercial project anyway, it’s just something I’m doing on the side for
my own benefit.)

So if I’m reading you correctly, this is where the “aspect of behavior that can occur N times” comes in, right? AFAIK the typical way to do this is via “macros” (ie. generating examples on the fly, typically keeping to examples of one assertion per iteration of the loop). So it’s just a minor tweak of what you’ve got there:

properties.each do |property|
it “is false if #{property} is different” do

end
end

If you mean by using a custom matcher in there, then yeah, I get you.
But as this is more extensive, shared examples (which prove many
individual pieces of behaviour) seemed a better fit than a matcher
(which are intended to prove just one).

But alas, this pattern won’t work with shared example groups. I don’t know of any way to pass the “properties” variable in this case, and even if you could it wouldn’t work anyway because, AFAIK, the shared example group is itself only evaluated once when the file is first read. It isn’t re-evaluted each time it is included in another example group. At least, that’s my understanding of it. I might be wrong about that. So, looks like you’re stuck with having multiple assertions inside a single “it” block.

Hmmm, if that’s the case, then this approach won’t work anyway. But
I’ll investigate… it depends on the implementation. This is one of
the things I want to find out.

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

Here the “standard” way of parametrizing this would be via blocks (“standard” in inverted commas because the ability to pass a block here is such a recent addition to RSpec).

Yeah, I’m doing that to get the necessary objects inside the shared
example. But as you pointed out, the scope inside example groups and
inside the examples themselves are very different.

Cheers
Ash


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

El 26/07/2010, a las 14:09, Ashley M.
escribió:

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

Personally I wouldn’t do this. It makes it harder for anybody coming to
your project to understand what’s going on, because they see this
“contract ‘foo’ do” construct and don’t know what it is unless they dig
into your spec_helper. If you really want the word contract to appear in
there I would just write the shared examples like this:

shared_examples_for ‘representation contract’ do

end

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

And if you go with names like “representation contract” then you might
want your alias to be just “it_satisifies” instead…

 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

So if I’m reading you correctly, this is where the “aspect of behavior
that can occur N times” comes in, right? AFAIK the typical way to do
this is via “macros” (ie. generating examples on the fly, typically
keeping to examples of one assertion per iteration of the loop). So it’s
just a minor tweak of what you’ve got there:

properties.each do |property|
it “is false if #{property} is different” do

end
end

But alas, this pattern won’t work with shared example groups. I don’t
know of any way to pass the “properties” variable in this case, and even
if you could it wouldn’t work anyway because, AFAIK, the shared example
group is itself only evaluated once when the file is first read. It
isn’t re-evaluted each time it is included in another example group. At
least, that’s my understanding of it. I might be wrong about that. So,
looks like you’re stuck with having multiple assertions inside a single
“it” block.

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

Here the “standard” way of parametrizing this would be via blocks
(“standard” in inverted commas because the ability to pass a block here
is such a recent addition to RSpec).

Cheers,
Wincent