Ashley Moran
2010-Jul-23 07:09 UTC
[rspec-users] Parameterised shared examples / metadata in examples (RSpec 2)
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
Wincent Colaiuta
2010-Jul-23 07:57 UTC
[rspec-users] Parameterised shared examples / metadata in examples (RSpec 2)
El 23/07/2010, a las 09:09, Ashley Moran escribi?:> 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.Recently commited (RSpec 2.0.0.beta.18) was the ability to pass a block to "it_should_behave_like", making the relation clearer; eg: describe MyStruct do it_should_behave_like ''a Struct'' do let(:struct) { MyStruct.new } end end I did ask about parametrizing that explicitly via metadata, but David feels that the block based approach is better; see the full thread here: http://github.com/rspec/rspec-core/issues/71 Wincent
Ashley Moran
2010-Jul-23 09:38 UTC
[rspec-users] Parameterised shared examples / metadata in examples (RSpec 2)
On Jul 23, 2010, at 8:57 am, Wincent Colaiuta wrote:> Recently commited (RSpec 2.0.0.beta.18) was the ability to pass a block to "it_should_behave_like", making the relation clearer; eg: > > describe MyStruct do > it_should_behave_like ''a Struct'' do > let(:struct) { MyStruct.new } > end > end > > I did ask about parametrizing that explicitly via metadata, but David feels that the block based approach is better; see the full thread here: > > http://github.com/rspec/rspec-core/issues/71And I only just tweeted about that ticket too! I didn''t review it all though, I just saw the aliasing (which I currently do by hand). That''s in beta 18 then? Pretty sure I just got that when I updated this morning. Will play around with it later... Cheers Ash -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashleymoran