Matt Wynne
2009-Apr-10 20:40 UTC
[rspec-users] message expectations without explicit receiver
On 10 Apr 2009, at 19:43, Barun Singh wrote:> A model I''m trying to spec looks something like this > > class MyClass > def methodA > do some stuff > methodB > end > > def methodB > do some stuff > end > end > > my spec looks something like this: > > x = MyClass.new > x.should_receive(:methodB) > lambda{ x.methodA } > > In the situation outlined above, the spec fails. But, if I change > the definition of methodA to be as follows the spec passes (note the > addition of "self" as the receiver): > > def methodA > do some stuff > self.methodB > end > > > The code doesn''t actually need for me to identify "self" as the > receiver on order for it to work. But rSpec seems to require this. > Is there an alternative way to write the spec so that it passes in > the original case (where I call methodB without an explicit receiver)?You''re going into some pretty messy territory here, trying to insert a mocking layer between two methods of the same class. Since most people who use RSpec wouldn''t want to do this, you''ve found that''s it not possible. I also suspect it would be quite difficult to write the mocking code, but that''s a different issue. I could write more about this, and I suspect others will, but for now I would suggest that you listen to your test and try to test the behaviour of the whole class, only mocking out collaborating objects. This will ensure there''s as little coupling as possible between the tests and the code, making that code easier to change. Here are a couple of things you could read up to see what I mean: http://www.jmock.org/oopsla2004.pdf http://www.patmaddox.com/blog/2008/10/27/testing-protected-and-private-methods-in-ruby Matt Wynne http://beta.songkick.com http://blog.mattwynne.net
David Chelimsky
2009-Apr-10 21:39 UTC
[rspec-users] message expectations without explicit receiver
On Fri, Apr 10, 2009 at 3:43 PM, Barun Singh <barunio at gmail.com> wrote:> A model I''m trying to spec looks something like this > > class MyClass > ? def methodA > ??? do some stuff > ??? methodB > ? end > > ? def methodB > ??? do some stuff > ? end > end > > my spec looks something like this: > > x = MyClass.new > x.should_receive(:methodB) > lambda{ x.methodA }Why the lambda? Is it ever called? The following works as expected: class Foo def bar baz end def baz end end describe "a message expectation on the same object" do it "works just fine" do foo = Foo.new foo.should_receive(:baz) foo.bar end end - David> > In the situation outlined above, the spec fails.? But, if I change the > definition of methodA to be as follows the spec passes (note the addition of > "self" as the receiver): > > def methodA > ? do some stuff > ? self.methodB > end > > > The code doesn''t actually need for me to identify "self" as the receiver on > order for it to work.? But rSpec seems to require this.? Is there an > alternative way to write the spec so that it passes in the original case > (where I call methodB without an explicit receiver)?Something> > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
David Chelimsky
2009-Apr-10 21:52 UTC
[rspec-users] message expectations without explicit receiver
On Fri, Apr 10, 2009 at 6:27 PM, Barun Singh <barunio at gmail.com> wrote:> Ok, well it seems that the answer to my direct question is that no, there''s > no way to test message expectations without an explicit receiver; thanks for > the responses. > > I suppose I should respond to the comments re: more general issue of testing > strategy [hopefully this doesn''t ignite too long of a thread]: > > I completely agree that the goal should be to disconnect testing from > implementation as much as possible.? In general, this requires finding the > simples set of N inputs to a method that cover all the basic behavior you > want to test for, and making sure that the appropriate output is generated > for all of those cases. > > This may work for the majority of methods you encounter; but it''s not always > practical.? Sometimes a method is complex enough that the N inputs you would > need to describe all behavior can be too large to be practical.? We want to > test thoroughly and to test in a way that''s easy to maintain.? Overall it is > easier to maintain tests that don''t rely at all on implementation, but it is > also easier to maintain a smaller number of tests than a very large number > of tests.? These two things have to be balanced. > > So, I do agree with the general rule, but it doesn''t apply in every > situation.? And, to be clear, this has nothing to do with testing protected > methods, which of course shouldn''t be done.? Here''s an example to illustrate > my point: > > Suppose methodA does the following: > ? * parse a data file uploaded by the user > ? * reformat the parsed data to deal with a variety of different allowable > configurations > ? * perform calculations on the collected data > > In the implementation, I might refactor the functionality into three smaller > methods (one for each bullet), and just call those three smaller methods > from within methodA.? Those other methods will also be public (they might > need to be called from elsewhere), and I test each of them separately.? So, > having tested all of the functionality described by those three bullets > separately, all I really care about in testing methodA is that it is > actually calling those three methods.? Otherwise, re-testing all that > functionality is not at all DRY. > > Even if I wanted to re-test all of the functionality, it can be next to > impossible.? Suppose I write 20 tests to fully spec out each of the three > methods called from within methodA (because there are 20 distinct sets of > behaviors that describe all the possible behaviors of each of those > methods).? In this case, if I wanted to test methodA without referencing any > internal logic at all, I might be required to write 20^3 = 8,000 tests to > fully cover all of the logic described by the 60 tests used to cover the > three subroutines.I responded to your initial post - I think we''re chasing a red herring here because what you want to be able to do *should* work. That said, the traditional view of what you''re describing now is to extract behaviour to separate objects, not just a separate methods, and then use mock objects in the examples. That solves the explosion of examples to cover edge cases without the risks associated with modifying the object being specified. Not to say this is law, nor that I never violate it myself. But pretty much the only time I do so is when a framework I''m using pushes me to do so. If the class being spec''d is a PORO, and all my code, I generally follow this guideline. HTH, David> > > > On Fri, Apr 10, 2009 at 4:42 PM, Nigel Thorne <rspec at nigelthorne.com> wrote: >> >> Hi Barun, >> Here is my take on this... >> Why are you mocking methods on the object under test? The aim of BDD is to >> specify the behaviour of the class. When you are mocking, the intention is >> to spec one object in?isolation. When you define expectations between >> objects you are specifying object interaction. >> Mocking method on the object under test ties your spec to your >> implementation. If you decided to implement ''methodA'' in some other way that >> lead to the same behaviour, then your specs shuould still pass. In your case >> they would not. >> Cheers >> Nigel >> >> 2009/4/11 Barun Singh <barunio at gmail.com> >>> >>> A model I''m trying to spec looks something like this >>> >>> class MyClass >>> ? def methodA >>> ??? do some stuff >>> ??? methodB >>> ? end >>> >>> ? def methodB >>> ??? do some stuff >>> ? end >>> end >>> >>> my spec looks something like this: >>> >>> x = MyClass.new >>> x.should_receive(:methodB) >>> lambda{ x.methodA } >>> >>> In the situation outlined above, the spec fails.? But, if I change the >>> definition of methodA to be as follows the spec passes (note the addition of >>> "self" as the receiver): >>> >>> def methodA >>> ? do some stuff >>> ? self.methodB >>> end >>> >>> >>> The code doesn''t actually need for me to identify "self" as the receiver >>> on order for it to work.? But rSpec seems to require this.? Is there an >>> alternative way to write the spec so that it passes in the original case >>> (where I call methodB without an explicit receiver)? >>> >>> _______________________________________________ >>> rspec-users mailing list >>> rspec-users at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >> >> >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Ashley Moran
2009-Apr-10 21:55 UTC
[rspec-users] message expectations without explicit receiver
On 10 Apr 2009, at 22:27, Barun Singh wrote:> This may work for the majority of methods you encounter; but it''s > not always practical. Sometimes a method is complex enough that the > N inputs you would need to describe all behavior can be too large to > be practical. We want to test thoroughly and to test in a way > that''s easy to maintain. Overall it is easier to maintain tests > that don''t rely at all on implementation, but it is also easier to > maintain a smaller number of tests than a very large number of > tests. These two things have to be balanced. > > So, I do agree with the general rule, but it doesn''t apply in every > situation. And, to be clear, this has nothing to do with testing > protected methods, which of course shouldn''t be done. Here''s an > example to illustrate my point: > > Suppose methodA does the following: > * parse a data file uploaded by the user > * reformat the parsed data to deal with a variety of different > allowable configurations > * perform calculations on the collected dataHi Barun The situation you describe is a violation of the Single Responsibility principle. Your *class*, never mind your method, is doing too much.> Even if I wanted to re-test all of the functionality, it can be next > to impossible. Suppose I write 20 tests to fully spec out each of > the three methods called from within methodA (because there are 20 > distinct sets of behaviors that describe all the possible behaviors > of each of those methods). In this case, if I wanted to test > methodA without referencing any internal logic at all, I might be > required to write 20^3 = 8,000 tests to fully cover all of the logic > described by the 60 tests used to cover the three subroutines.What you need to do is figure out how you can isolate parts of the algorithm and spec them as separate objects. That way you can mock out the other bits, and cut down the permutations you need to cover to prove the app still works. Excessive build-up of logic branches is something I struggled with for a long time, and it still bit me recently. But, in my experience, it''s always just a code/spec smell. As soon as the specs get too complex to understand, find a simpler way of expressing you problem. This can be either by refactoring with standard OO techniques, or - if that fails to reduce the complexity - fundamentally re-modelling the problem. HTH Ashley -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashleymoran http://aviewfromafar.net/ http://twitter.com/ashleymoran
David Chelimsky
2009-Apr-10 21:57 UTC
[rspec-users] message expectations without explicit receiver
On Fri, Apr 10, 2009 at 6:55 PM, Ashley Moran <ashley.moran at patchspace.co.uk> wrote:> On 10 Apr 2009, at 22:27, Barun Singh wrote: >> >> This may work for the majority of methods you encounter; but it''s not >> always practical. ?Sometimes a method is complex enough that the N inputs >> you would need to describe all behavior can be too large to be practical. >> ?We want to test thoroughly and to test in a way that''s easy to maintain. >> ?Overall it is easier to maintain tests that don''t rely at all on >> implementation, but it is also easier to maintain a smaller number of tests >> than a very large number of tests. ?These two things have to be balanced. >> >> So, I do agree with the general rule, but it doesn''t apply in every >> situation. ?And, to be clear, this has nothing to do with testing protected >> methods, which of course shouldn''t be done. ?Here''s an example to illustrate >> my point: >> >> Suppose methodA does the following: >> ?* parse a data file uploaded by the user >> ?* reformat the parsed data to deal with a variety of different allowable >> configurations >> ?* perform calculations on the collected data > > Hi Barun > > The situation you describe is a violation of the Single Responsibility > principle. ?Your *class*, never mind your method, is doing too much.Agreed. FWIW.> >> Even if I wanted to re-test all of the functionality, it can be next to >> impossible. ?Suppose I write 20 tests to fully spec out each of the three >> methods called from within methodA (because there are 20 distinct sets of >> behaviors that describe all the possible behaviors of each of those >> methods). ?In this case, if I wanted to test methodA without referencing any >> internal logic at all, I might be required to write 20^3 = 8,000 tests to >> fully cover all of the logic described by the 60 tests used to cover the >> three subroutines. > > > What you need to do is figure out how you can isolate parts of the algorithm > and spec them as separate objects. ?That way you can mock out the other > bits, and cut down the permutations you need to cover to prove the app still > works. > > Excessive build-up of logic branches is something I struggled with for a > long time, and it still bit me recently. ?But, in my experience, it''s always > just a code/spec smell. ?As soon as the specs get too complex to understand, > find a simpler way of expressing you problem. ?This can be either by > refactoring with standard OO techniques, or - if that fails to reduce the > complexity - fundamentally re-modelling the problem. > > HTH > > Ashley > > -- > http://www.patchspace.co.uk/ > http://www.linkedin.com/in/ashleymoran > http://aviewfromafar.net/ > http://twitter.com/ashleymoran > > > > > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >