Hi

Warning: this goes on quite a bit.  It contains early-morning caffeinated 
ramblings and many "hmmm I wonder what this does..." snippets.

I'm looking for the best way to parameterise shared examples.  Imagine (as an 
academic example...) you were doing it for subclasses of Struct instances (a 
more realistic example might be ActiveRecord subclasses, or DataMapper 
resources), such as:

  class MyStruct < Struct.new(:a, :b)  
  end

  class MyOtherStruct < Struct.new(:foo, :bar)
  end


I've seen it done with #let, eg:

  shared_examples_for "a Struct" do
    it "has methods" do
      properties.each do |property|
        struct.should respond_to(property)
      end
    end
  end

  describe MyStruct do
    let(:struct) { MyStruct.new }
    let(:properties) { [:a, :b] }
    it_should_behave_like "a Struct"
  end

  describe MyOtherStruct do
    let(:struct) { MyOtherStruct.new }
    let(:properties) { [:foo, :bar] }
    it_should_behave_like "a Struct"
  end

Which is not a bad solution, but does feel a bit too much like using (scoped) 
global variables for my liking.  There's no explicit association between the 
shared examples and their parameters (and the arguments actually passed in each 
example group.


So I started to wonder if this could be done with metadata.  My first naive 
stab was this:

  describe MyStruct do
    it_should_behave_like "a Struct", properties: [:a, :b]
  end

But this fails:

  Could not find shared example group named {:properties=>[:a, :b]}

Anyway, I dug in a bit and found that the metadata is only available to the 
example group anyway, not the examples themselves.  So you can't do:

  describe MyStruct, properties: [:a, :b] do
    let(:struct) { MyStruct.new }
    it "has methods" do
      metadata[:properties].each do |property|
        struct.should respond_to(property)
      end
    end
  end

But (more digging), you can do this:  

  describe MyStruct, properties: [:a, :b] do
    let(:struct) { MyStruct.new }
    it "has methods" do
      example.metadata[:properties].each do |property|
        struct.should respond_to(property)
      end
    end
  end

Which means I can get this close to my original dreamed-up syntax:

  shared_examples_for "a Struct with metadata" do
    it "has methods" do
      example.metadata[:properties].each do |property|
        struct.should respond_to(property)
      end
    end
  end

  describe MyStruct, properties: [:a, :b] do
    let(:struct) { MyStruct.new }
    it_should_behave_like "a Struct with metadata"
  end

I don't object so much to having "struct" floating around, as it's fairly safe 
to say all the shared examples will depend on #struct being available.  
Although, arguably, #subject would be better:

  shared_examples_for "a subject Struct with metadata" do
    it "has methods" do
      example.metadata[:properties].each do |property|
        subject.should respond_to(property)
      end
    end
  end

  describe MyStruct, properties: [:a, :b] do
    subject { MyStruct.new }
    it_should_behave_like "a subject Struct with metadata"
  end

or even:

  shared_examples_for "a subject Struct with metadata" do
    metadata[:properties].each do |property|
      it { should respond_to(property) }
    end
  end

  describe MyStruct, properties: [:a, :b] do
    subject { MyStruct.new }
    it_should_behave_like "a subject Struct with metadata"
  end

I tried to be a bit clever to see if I could clean up the example definitions 
in the shared spec, but I got this far before hitting weirdness that was beyond 
my understanding of RSpec (and the reach of my spade...).  But, this was a bit 
of a tangent anyway:

  shared_examples_for "a subject Struct with metadata" do
    metadata[:params].each { |key, value| define_method(key) { value } }
    
    p self.inspect # outputs nil (!!!)
    
    properties.each do |property|
      it { should respond_to(property) }
    end
  end

  describe MyStruct, params: {properties: [:a, :b]} do
    subject { MyStruct.new }
    it_should_behave_like "a subject Struct with metadata"
  end


Sooooo... after all this, I just wondered if anyone had any ideas what the best 
way to achieve this is, and how it could be extended.

For example, would there be any merit in being able to write: 

  it_should_behave_like "a Struct", properties: [:a, :b]

?

Also I figure that as the metadata system is new, it's potentially unfinished 
and/or in flux.  What are the plans/intentions/opportunities for expansion for 
it?

Cheers
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