hi, i want to make a behavior shared between models, the examples
need to create new instances etc. Is there a way to pass the model
class to the shared behavior?
On Dec 18, 2007 11:39 AM, Jonathan L. [email protected] wrote:
hi, i want to make a behavior shared between models, the examples need to
create new instances etc. Is there a way to pass the model class to the
shared behavior?
We’re calling these shared example groups now.
The way to affect the state within a shared example group is to use
before(:each) to set an instance variable:
shared_examples_for “models that do stuff” do
before(:each) do
@instance = @class.new
end
it “should do something” do
@instance.should do_something
end
end
describe Foo do
before(:each) do
@class = Foo
end
it_should_behave_like “models that do stuff”
end
Cheers,
David
On Dec 18, 2007, at 12:39 PM, Jonathan L. wrote:
hi, i want to make a behavior shared between models, the examples
need to create new instances etc. Is there a way to pass the model
class to the shared behavior?
Nope. The only way to do it right now is with an instance variable.
The shared behaviours can be thought of modules which are included
into the current behaviour - so it should also have access to any
methods defined in the example (group) in which it is shared.
Scott
On Dec 18, 2007 3:37 PM, Scott T. [email protected]
wrote:
The
shared behaviours can be thought of modules which are included into the
current behaviour - so it should also have access to any methods defined in
the example (group) in which it is shared.
You don’t even have to think of it that way - that’s actually what
they are - modules that get included. You can actually assign them to
a constant and just use Ruby include.
On Dec 18, 2007, at 4:42 PM, David C. wrote:
they are - modules that get included. You can actually assign them to
a constant and just use Ruby include.
Although we’ve talked about the implementation changing, which would
cause those who use them as modules to have broken specs.
Scott
On Dec 18, 2007 3:53 PM, Scott T. [email protected]
wrote:
defined in
the example (group) in which it is shared.You don’t even have to think of it that way - that’s actually what
they are - modules that get included. You can actually assign them to
a constant and just use Ruby include.Although we’ve talked about the implementation changing, which would
cause those who use them as modules to have broken specs.
It would, but we stopped talking about it
It actually works quite well this way. The change came up, iirc, in
the conversation about parameterizing it_should_behave_like, right?
I’ve got a solution for that worked out, but I haven’t applied it yet
because I want to discuss it a bit:
You’d use it like this:
shared_examples_for “anything” do
it “should do stuff” do
@thing.should do_stuff
end
end
describe SpecificThing do
it_should_behave_like “anything”, :thing => SpecificThing.new
end
Now comes the tricky part. This can work one of two ways. In either
case it turns :thing into an instance variable @thing. The question is
whether that instance variable is assigned in the scope of the
SpecificThing example group or in a scope created especially to run
the shared examples. Each choice presents usability confusion in my
view.
Imagine this scenario:
shared_examples_for “anything” do
it “should do stuff” do
@thing.should do_stuff
end
it “should be like another thing” do
@thing.should be_like(@other_thing)
end
end
describe SpecificThing do
before(:each) do
@thing = SpecificThing.new
end
it_should_behave_like “anything”, :other_thing => SpecificThing.new
it “should not be unlike other thing” do
@thing.should_not be_unlike(@other_thing)
end
end
If @other_thing is defined in the context of a separate scope just to
run the shared examples, the shared examples won’t know about @thing.
If @other_thing in the scope of the example group created by “describe
SpecificThing,” then it’s just confusing to look at - both
before(:each) and it_should_behave_like are creating instance
variables in the current scope, and it is NOT clear that is what is
happening.
If we go w/ the separate scope, then the metaphor of
it_should_behave_like is wrong, because we’re not talking about
something in the current scope that should behave like something else
anymore. We’d probably want a different construct to create these,
even though the underlying mechanism would be the same.
Thoughts?
David
On Dec 18, 2007, at 5:12 PM, David C. wrote:
into the
cause those who use them as modules to have broken specs.
shared_examples_for “anything” do
case it turns :thing into an instance variable @thing. The question is
endbefore(:each) and it_should_behave_like are creating instance
variables in the current scope, and it is NOT clear that is what is
happening.If we go w/ the separate scope, then the metaphor of
it_should_behave_like is wrong, because we’re not talking about
something in the current scope that should behave like something else
anymore. We’d probably want a different construct to create these,
even though the underlying mechanism would be the same.Thoughts?
How about this:
Use a separate scope, but copy the instance variables from the
calling example group. Any parameters passed into the shared example
group will be methods which wrap the values given. This way we
don’t have instance variable naming conflicts (like @other_thing, in
the example above).
On the other hand, this would also mean that shared example groups
wouldn’t have the ability to call methods in the calling example group.
Thoughts?
Scott
On Dec 18, 2007 4:22 PM, Scott T. [email protected]
wrote:
On Dec 18, 2007 3:37 PM, Scott T.
them to
because I want to discuss it a bit:
it_should_behave_like “anything”, :thing => SpecificThing.newbefore(:each) do
run the shared examples, the shared examples won’t know about @thing.
anymore. We’d probably want a different construct to create these,
don’t have instance variable naming conflicts (like @other_thing, in
the example above).On the other hand, this would also mean that shared example groups
wouldn’t have the ability to call methods in the calling example group.Thoughts?
Copying instance variables around is nasty business. In fact, we
eliminated that from shared behaviours by making them modules that get
included! So I would view that as a step backwards from an
implementation standpoint, and it still feels like voodoo from a
usability standpoint.
On Dec 18, 2007 4:25 PM, David C. [email protected] wrote:
On Dec 18, 2007, at 4:42 PM, David C. wrote:
You don’t even have to think of it that way - that’s actually what
the conversation about parameterizing it_should_behave_like, right?it_should_behave_like is wrong, because we’re not talking about
calling example group. Any parameters passed into the shared example
eliminated that from shared behaviours by making them modules that get
included! So I would view that as a step backwards from an
implementation standpoint, and it still feels like voodoo from a
usability standpoint.
Well - THAT was nothing but negative.
Let me add some positive to balance things:
As I think about it, I’m more inclined to make this a completely
different method, as I suggested as an option earlier. Something that
says “run in another scope with these variables.” That way there’s no
magic and consequently no confusion.
Suggestions?
On Dec 18, 2007 4:30 PM, Rick DeNatale [email protected] wrote:
before(:each) to set an instance variable:
describe Foo do
This helps me a lot.
it “should set @outer to @inner” do
endit "should be set outer to 1" do
expected: 1,
4 examples, 2 failures
That’s actually as it should be. The common use case for nested is
that you create something in the outer group and expand its definition
in the inner group.
describe Thing do
before(:each) { @thing = Thing.new }
describe “with special magic powers” do
before(:each) { @thing.grant_special_magic_powers }
it “should be able to conjure up world peace” do
@thing.should be_able_to_conjure_up_world_peace
end
end
end
This requires running the befores in the outer groups first, which is
the opposite of what your example would require.
Make sense?
David
On 12/18/07, David C. [email protected] wrote:
before(:each) do
@class = Foo
end
it_should_behave_like “models that do stuff”
end
So I guess this means that the before block for the shared example
group gets executed after the example group using it. I don’t recall
seeing this documented, although I’m sure that it is.
This helps me a lot.
Can you do something similar with the new nested example groups? It
doesn’t seem so.
describe “Shared”, :shared => true do
before(:each) do
@outer = @inner
end
it “should set @outer to @inner” do
@outer.should == @inner
end
end
describe “Sharer” do
it_should_behave_like “Shared”
it “should set @outer to 1” do
@inner = 1
end
end
describe “Outer” do
before(:each) do
@outer = @inner
end
describe “inner one” do
before(:each) do
@inner = 1
end
it "should be set outer to 1" do
@outer.should == 1
end
end
describe “inner two” do
before(:each) do
@inner = 2
end
it "should be set outer to 1" do
@outer.should == 2
end
end
end
k$ spec ~/nestedspec.rb
FF…
‘inner one should be set outer to 1’ FAILED
expected: 1,
got: nil (using ==)
/Users/rick/nestedspec.rb:16:
‘inner two should be set outer to 1’ FAILED
expected: 2,
got: nil (using ==)
/Users/rick/nestedspec.rb:26:
Finished in 0.00627 seconds
4 examples, 2 failures
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
On 12/18/07, David C. [email protected] wrote:
end
shared_examples_for “anything” do
@thing = SpecificThing.neweven though the underlying mechanism would be the same.
Thoughts?
How about something like (following the example of things like the
:include option in ActiveRecord::Base#find:
it_should_behave_like “anything”, :except_that => { :thing =>
SpecificThing.new}
–
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
On Dec 18, 2007 4:38 PM, Rick DeNatale [email protected] wrote:
How about something like (following the example of things like the
:include option in ActiveRecord::Base#find:it_should_behave_like “anything”, :except_that => { :thing =>
SpecificThing.new}
That’s a little better, but I’m really leaning towards a different
method name. Don’t know what it is yet. Awaiting suggestions
On 12/18/07, David C. [email protected] wrote:
@thing.should be_able_to_conjure_up_world_peace end
end
endThis requires running the befores in the outer groups first, which is
the opposite of what your example would require.Make sense?
Yes and it helps clarify my questions about how nested groups were
different, if at all, from shared groups.
–
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
On Dec 18, 2007 4:45 PM, Scott T. [email protected]
wrote:
On Dec 18, 2007, at 5:12 PM, David C. wrote:
shared behaviours can be thought of modules which are included
because I want to discuss it a bit:
it_should_behave_like “anything”, :thing => SpecificThing.new
Imagine this scenario:
describe SpecificThing dohappening.
wouldn’t have the ability to call methods in the calling example
anyway - I end up aliasing it in a few different forms:
it_should_behave_like_a
it_should_behave_like_theand so on.
Anyway - how do the nested behaviours work right now? Are instance
variables being copied? Methods?
They are subclasses!!! It’s pretty sweet. We can thank Brian (and
Aslak, too) for getting us there.
Basically there is no copying, just inheriting or, in the case of
shared, mixing in. Ruby does the rest for free.
On Dec 18, 2007, at 5:31 PM, David C. wrote:
the example (group) in which it is shared.
shared_examples_for “anything” do
case it turns :thing into an instance variable @thing. The
@thing.should do_stuff
it_should_behave_like “anything”, :other_thing =>
@thing.
something in the current scope that should behave like something
calling example group. Any parameters passed into the sharedAs I think about it, I’m more inclined to make this a completely
different method, as I suggested as an option earlier. Something that
says “run in another scope with these variables.” That way there’s no
magic and consequently no confusion.
I’m with you on that one. I don’t like “it_should_behave_like” much
anyway - I end up aliasing it in a few different forms:
it_should_behave_like_a
it_should_behave_like_the
and so on.
Anyway - how do the nested behaviours work right now? Are instance
variables being copied? Methods?
Scott
On Dec 18, 2007 4:46 PM, Rick DeNatale [email protected] wrote:
before(:each) { @thing.grant_special_magic_powers }
Yes and it helps clarify my questions about how nested groups were
different, if at all, from shared groups.
My response to scott’s mail should enlighten that a bit as well.
Nested groups are subclasses of the outer groups.
Shared groups get mixed in.