Chuck Remes
2010-Jul-14 14:26 UTC
[rspec-users] set expectation using self in constructor?
I find myself using this pattern quite a bit. rspec 1.30 ruby 1.9.1, 1.9.2-rc2, jruby 1.51 all on osx 10.6.4 class Foo def initialize @bar = Bar.new end end context "init" do it "should allocate a helper class Bar" do Bar.should_receive(:new) Foo.new end end That all works well and as expected. Where I get stuck is when I change the signature for Bar to accept an argument from Foo like so: class Foo def initialize @bar = Bar.new self end end # try 1 context "init" do it "should allocate a helper class Bar" do Bar.should_receive(:new).with(self) # self refers to rspec here Foo.new end end # try 2 context "init" do let(:foo) { Foo.new } it "should allocate a helper class Bar" do Bar.should_receive(:new).with(foo) # foo is a different instance Foo.new end end # try 3 context "init" do it "should allocate a helper class Bar" do Bar.should_receive(:new).with(instance_of(Foo)) # works but seems wrong Foo.new end end I have tried lots of techniques for setting an argument expectation in my spec, but none of them work completely. How do others solve this? Or have I discovered a spec anti-pattern? If this is an anti-pattern, what is the suggested programming technique to avoid it? cr
Rick DeNatale
2010-Jul-14 15:18 UTC
[rspec-users] set expectation using self in constructor?
On Wed, Jul 14, 2010 at 10:26 AM, Chuck Remes <cremes.devlist at mac.com> wrote:> I find myself using this pattern quite a bit. > > rspec 1.30 > ruby 1.9.1, 1.9.2-rc2, jruby 1.51 all on osx 10.6.4 > > class Foo > ?def initialize > ? ?@bar = Bar.new > ?end > end > > context "init" do > ?it "should allocate a helper class Bar" do > ? ?Bar.should_receive(:new) > ? ?Foo.new > ?end > end > > That all works well and as expected. Where I get stuck is when I change the signature for Bar to accept an argument from Foo like so: > > class Foo > ?def initialize > ? ?@bar = Bar.new self > ?end > end > > # try 1 > context "init" do > ?it "should allocate a helper class Bar" do > ? ?Bar.should_receive(:new).with(self) ?# self refers to rspec here > ? ?Foo.new > ?end > end > > # try 2 > context "init" do > ?let(:foo) { Foo.new } > > ?it "should allocate a helper class Bar" do > ? ?Bar.should_receive(:new).with(foo) ?# foo is a different instance > ? ?Foo.new > ?end > end > > # try 3 > context "init" do > ?it "should allocate a helper class Bar" do > ? ?Bar.should_receive(:new).with(instance_of(Foo)) # works but seems wrong > ? ?Foo.new > ?end > end > > I have tried lots of techniques for setting an argument expectation in my spec, but none of them work completely. How do others solve this? Or have I discovered a spec anti-pattern? > > If this is an anti-pattern, what is the suggested programming technique to avoid it?The problem is that the instance of Foo that you are testing for doesn''t exist until you call Foo.new. I''d change this to spec the results rather than the implementation: e.g. foo = Foo.new foo.bar.should == foo Assuming that foo and bar have public accessors for bar and foo respectively. If not you can use instance_variable_get to ''get'' around that. HTH -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Github: http://github.com/rubyredrick Twitter: @RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale
On 14 Jul 2010, at 15:26, Chuck Remes wrote:> I find myself using this pattern quite a bit. > > rspec 1.30 > ruby 1.9.1, 1.9.2-rc2, jruby 1.51 all on osx 10.6.4 > > class Foo > def initialize > @bar = Bar.new > end > end > > context "init" do > it "should allocate a helper class Bar" do > Bar.should_receive(:new) > Foo.new > end > end > > That all works well and as expected. Where I get stuck is when I change the signature for Bar to accept an argument from Foo like so: > > class Foo > def initialize > @bar = Bar.new self > end > end > > # try 1 > context "init" do > it "should allocate a helper class Bar" do > Bar.should_receive(:new).with(self) # self refers to rspec here > Foo.new > end > end > > # try 2 > context "init" do > let(:foo) { Foo.new } > > it "should allocate a helper class Bar" do > Bar.should_receive(:new).with(foo) # foo is a different instance > Foo.new > end > end > > # try 3 > context "init" do > it "should allocate a helper class Bar" do > Bar.should_receive(:new).with(instance_of(Foo)) # works but seems wrong > Foo.new > end > end > > I have tried lots of techniques for setting an argument expectation in my spec, but none of them work completely. How do others solve this? Or have I discovered a spec anti-pattern? > > If this is an anti-pattern, what is the suggested programming technique to avoid it?You can do this, by using a test spy to remember the value of foo passed into the stubbed constructor and then later comparing it: let(:foo) { Foo.new } it "should allocate a helper class Foo" do actual_foo = Bar.should_receive(:new) do |the_foo| the_foo end actual_foo.should == foo end Whether you want to do this though, is another question. I think it''s a bit of an anti-pattern personally. I''d probably let acceptance tests catch mistakes in this kind of thing, and concentrate on speccing the interaction between Foo and Bar once you''ve got the instances spun up.> > cr > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-userscheers, Matt http://blog.mattwynne.net +44(0)7974 430184
Chuck Remes
2010-Jul-15 13:42 UTC
[rspec-users] set expectation using self in constructor?
On Jul 14, 2010, at 4:20 PM, Matt Wynne wrote:> > You can do this, by using a test spy to remember the value of foo passed into the stubbed constructor and then later comparing it: > > let(:foo) { Foo.new } > > it "should allocate a helper class Foo" do > actual_foo = Bar.should_receive(:new) do |the_foo| > the_foo > end > actual_foo.should == foo > end > > Whether you want to do this though, is another question. I think it''s a bit of an anti-pattern personally. I''d probably let acceptance tests catch mistakes in this kind of thing, and concentrate on speccing the interaction between Foo and Bar once you''ve got the instances spun up.This is an interesting technique. I didn''t realize that using the block form of expectations/matchers returned a value. Thanks for the tip! cr
Chuck Remes
2010-Jul-15 13:55 UTC
[rspec-users] set expectation using self in constructor?
On Jul 14, 2010, at 4:20 PM, Matt Wynne wrote:> > You can do this, by using a test spy to remember the value of foo passed into the stubbed constructor and then later comparing it: > > let(:foo) { Foo.new } > > it "should allocate a helper class Foo" do > actual_foo = Bar.should_receive(:new) do |the_foo| > the_foo > end > actual_foo.should == foo > end > > Whether you want to do this though, is another question. I think it''s a bit of an anti-pattern personally. I''d probably let acceptance tests catch mistakes in this kind of thing, and concentrate on speccing the interaction between Foo and Bar once you''ve got the instances spun up.For those following along at home, this exact technique did not work. The +actual_foo+ variable was holding some mock value instead of the real self value. I fixed it by assigning to a variable inside the block that had been defined outside the block (avoiding block scope issues). let(:foo) { Foo.new } it "should allocate a helper class Foo" do actual_foo = nil Bar.should_receive(:new) do |the_foo| actual_foo = the_foo end actual_foo.should == foo end This succeeds. Thanks to all for your help. cr
On 15 Jul 2010, at 14:55, Chuck Remes wrote:> > On Jul 14, 2010, at 4:20 PM, Matt Wynne wrote: > >> >> You can do this, by using a test spy to remember the value of foo passed into the stubbed constructor and then later comparing it: >> >> let(:foo) { Foo.new } >> >> it "should allocate a helper class Foo" do >> actual_foo = Bar.should_receive(:new) do |the_foo| >> the_foo >> end >> actual_foo.should == foo >> end >> >> Whether you want to do this though, is another question. I think it''s a bit of an anti-pattern personally. I''d probably let acceptance tests catch mistakes in this kind of thing, and concentrate on speccing the interaction between Foo and Bar once you''ve got the instances spun up. > > For those following along at home, this exact technique did not work. The +actual_foo+ variable was holding some mock value instead of the real self value.Sorry. Do we think this is a bug in rspec? I''d have expected the block to return the last evaluated value. But is that just me?> > I fixed it by assigning to a variable inside the block that had been defined outside the block (avoiding block scope issues). > > let(:foo) { Foo.new } > > it "should allocate a helper class Foo" do > actual_foo = nil > Bar.should_receive(:new) do |the_foo| > actual_foo = the_foo > end > > actual_foo.should == foo > end > > This succeeds. > > Thanks to all for your help. > > cr > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-userscheers, Matt http://blog.mattwynne.net +44(0)7974 430184
On 16 Jul 2010, at 10:29, Matt Wynne wrote:>> For those following along at home, this exact technique did not work. The +actual_foo+ variable was holding some mock value instead of the real self value. > Sorry. Do we think this is a bug in rspec? I''d have expected the block to return the last evaluated value. But is that just me?It doesn''t look like a bug to me -- I expect #should_receive to return a message expectation, e.g. so that I can call #with/#and_return on it, and it would seem weird for it to behave otherwise when a block is supplied. Cheers, -Tom
David Chelimsky
2010-Jul-16 11:09 UTC
[rspec-users] set expectation using self in constructor?
On Jul 16, 2010, at 4:45 AM, Tom Stuart wrote:> On 16 Jul 2010, at 10:29, Matt Wynne wrote: >>> For those following along at home, this exact technique did not work. The +actual_foo+ variable was holding some mock value instead of the real self value. >> Sorry. Do we think this is a bug in rspec? I''d have expected the block to return the last evaluated value. But is that just me? > > It doesn''t look like a bug to me -- I expect #should_receive to return a message expectation, e.g. so that I can call #with/#and_return on it, and it would seem weird for it to behave otherwise when a block is supplied.Correct. The block syntax is an alternative to and_returns, and_yields, and_raises, etc. The block is evaluated lazily, when the stubbed or mocked method is invoked. HTH, David> Cheers, > -Tom > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
On 16 Jul 2010, at 10:45, Tom Stuart wrote:> On 16 Jul 2010, at 10:29, Matt Wynne wrote: >>> For those following along at home, this exact technique did not work. The +actual_foo+ variable was holding some mock value instead of the real self value. >> Sorry. Do we think this is a bug in rspec? I''d have expected the block to return the last evaluated value. But is that just me? > > It doesn''t look like a bug to me -- I expect #should_receive to return a message expectation, e.g. so that I can call #with/#and_return on it, and it would seem weird for it to behave otherwise when a block is supplied. > > Cheers, > -TomOf course, silly me.> _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-userscheers, Matt http://blog.mattwynne.net +44(0)7974 430184