Hi everyone, Do anyone know if there''s a way to spec protected methods ? Thanks in advance -- Bastien
On 23 Oct 2008, at 10:16, Bastien wrote:> Hi everyone, > Do anyone know if there''s a way to spec protected methods ? > Thanks in advanceSorry if this sounds preachy, but I would always advise you to factor out the code in the method into another class, then test that class. If the behaviour of the method is so complex you need to test it on it''s own, it deserves to be in a class all by itself. This is what we call, ''listening to your specs'' - if it''s hard to spec, you''ve probably got the design wrong. HTH, Matt
I totally agree with the ''''listening to your specs" concepts and always divide my code into small methods easier to spec. Now let me get you right, here''s my code : class Survey < ActiveRecord::Base has_many :participants ... def generate_reports ... sub_total = sub_total(participants.reports) ... end protected def sub_total(reports) sub_total = 0 reports.collect do |report| sub_total = sub_total + report.score end return sub_total end end I m already specing the "generate_reports" methods with a mock on "sub_total", but I would also like to spec that method. I''ve put this method into protected because it doesn''t make sens to call it from outside this class. But you''re arguing that I should put that method into a lib ? I m not sure it would make sense as this method is very specific to that class, and would actually be easy to spec. Except that it''s not possible to call a protected method from a spec as far as I know, correct me if I m wrong. (I maybe didn''t explain my problem well in the first place) Regards -- Bastien On Oct 23, 12:37?pm, Matt Wynne <m... at mattwynne.net> wrote:> On 23 Oct 2008, at 10:16, Bastien wrote: > > > Hi everyone, > > Do anyone know if there''s a way to spec protected methods ? > > Thanks in advance > > Sorry if this sounds preachy, but I would always advise you to factor ? > out the code in the method into another class, then test that class. > > If the behaviour of the method is so complex you need to test it on ? > it''s own, it deserves to be in a class all by itself. > > This is what we call, ''listening to your specs'' - if it''s hard to ? > spec, you''ve probably got the design wrong. > > HTH, > Matt > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
On Thu, Oct 23, 2008 at 7:49 AM, Bastien <bastien.vaucher at gmail.com> wrote:> I m already specing the "generate_reports" methods with a mock on > "sub_total", but I would also like to spec that method. I''ve put this > method into protected because it doesn''t make sens to call it from > outside this class. But you''re arguing that I should put that method > into a lib ? I m not sure it would make sense as this method is very > specific to that class, and would actually be easy to spec. Except > that it''s not possible to call a protected method from a spec as far > as I know, correct me if I m wrong. (I maybe didn''t explain my problem > well in the first place)If you''re somehow mocking the sub_total method while specing the generate_reports method, don''t do that. Don''t stub/mock the thing that you''re specing, only stub/mock its collaborators, i.e., its dependencies. I would either verify via generate_reports that the sub totals on the report are correct, or I''d factor out something like a ReportGenerator class and spec it. Then, I''d stub/mock out the ReportGenerator in the specs for the Survey class. Regards, Craig -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20081023/41604f84/attachment.html>
Bastien <bastien.vaucher at gmail.com> writes:> I totally agree with the ''''listening to your specs" concepts and > always divide my code into small methods easier to spec. > Now let me get you right, here''s my code : > > class Survey < ActiveRecord::Base > has_many :participants > ... > def generate_reports > ... > sub_total = sub_total(participants.reports) > ... > end > > protected > > def sub_total(reports) > sub_total = 0 > reports.collect do |report| > sub_total = sub_total + report.score > end > return sub_total > end > > end > > I m already specing the "generate_reports" methods with a mock on > "sub_total"Why are you mocking #sub_total? Pat
On Thu, Oct 23, 2008 at 6:49 AM, Bastien <bastien.vaucher at gmail.com> wrote:> I totally agree with the ''''listening to your specs" concepts and > always divide my code into small methods easier to spec. > Now let me get you right, here''s my code : > > class Survey < ActiveRecord::Base > has_many :participants > ... > def generate_reports > ... > sub_total = sub_total(participants.reports) > ... > end > > protected > > def sub_total(reports) > sub_total = 0 > reports.collect do |report| > sub_total = sub_total + report.score > end > return sub_total > end > > end > > I m already specing the "generate_reports" methods with a mock on > "sub_total", but I would also like to spec that method. I''ve put this > method into protected because it doesn''t make sens to call it from > outside this class. But you''re arguing that I should put that method > into a lib ? I m not sure it would make sense as this method is very > specific to that class, and would actually be easy to spec. Except > that it''s not possible to call a protected method from a spec as far > as I know, correct me if I m wrong. (I maybe didn''t explain my problem > well in the first place)This sort of decision is always a balancing act. Leaving it where it is means you have to either * unprotect it, which you clearly prefer not to do * expose it for you code example using clever ruby trickery * give up testing it directly * move it to another class, adding conceptual weight to your system None of these are perfect and all come with some tradeoff. I''ll confess that, in practice, I''d probably not test this directly. More often than not, for me, the sub_total method is a result of refactoring the generate_reports method, which by that time would have at least two code examples in which the only difference is the sum of scores of the reports. In this case, however, assuming participants and reports are AR backed models, you don''t really need to introduce a new class: subtotal = participants.reports.sum(''score'') Cheers, David> > Regards > > -- Bastien > > On Oct 23, 12:37 pm, Matt Wynne <m... at mattwynne.net> wrote: >> On 23 Oct 2008, at 10:16, Bastien wrote: >> >> > Hi everyone, >> > Do anyone know if there''s a way to spec protected methods ? >> > Thanks in advance >> >> Sorry if this sounds preachy, but I would always advise you to factor >> out the code in the method into another class, then test that class. >> >> If the behaviour of the method is so complex you need to test it on >> it''s own, it deserves to be in a class all by itself. >> >> This is what we call, ''listening to your specs'' - if it''s hard to >> spec, you''ve probably got the design wrong. >> >> HTH, >> Matt >> _______________________________________________ >> rspec-users mailing list >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Thanks for all your answers. I was just wondering how about using send in my specs ? It would bypass the protected limitation without altering my code : @survey.send( :sub_total, [@mock_report] ) Is that the kind of trickery you were referring to David ? Would that be bad practice ? Regards -- Bastien On Oct 23, 3:51?pm, "David Chelimsky" <dchelim... at gmail.com> wrote:> On Thu, Oct 23, 2008 at 6:49 AM, Bastien <bastien.vauc... at gmail.com> wrote: > > I totally agree with the ''''listening to your specs" concepts and > > always divide my code into small methods easier to spec. > > Now let me get you right, here''s my code : > > > class Survey < ActiveRecord::Base > > ?has_many :participants > > ?... > > ?def generate_reports > > ? ?... > > ? ?sub_total = sub_total(participants.reports) > > ? ?... > > ?end > > > protected > > > ?def sub_total(reports) > > ? ?sub_total = 0 > > ? ?reports.collect do |report| > > ? ? ?sub_total = sub_total + report.score > > ? ?end > > ? return sub_total > > ?end > > > end > > > I m already specing the "generate_reports" methods with a mock on > > "sub_total", but I would also like to spec that method. I''ve put this > > method into protected because it doesn''t make sens to call it from > > outside this class. But you''re arguing that I should put that method > > into a lib ? I m not sure it would make sense as this method is very > > specific to that class, and would actually be easy to spec. Except > > that it''s not possible to call a protected method from a spec as far > > as I know, correct me if I m wrong. (I maybe didn''t explain my problem > > well in the first place) > > This sort of decision is always a balancing act. Leaving it where it > is means you have to either > > * unprotect it, which you clearly prefer not to do > * expose it for you code example using clever ruby trickery > * give up testing it directly > * move it to another class, adding conceptual weight to your system > > None of these are perfect and all come with some tradeoff. > > I''ll confess that, in practice, I''d probably not test this directly. > More often than not, for me, the sub_total method is a result of > refactoring the generate_reports method, which by that time would have > at least two code examples in which the only difference is the sum of > scores of the reports. > > In this case, however, assuming participants and reports are AR backed > models, you don''t really need to introduce a new class: > > subtotal = participants.reports.sum(''score'') > > Cheers, > David > > > > > > > Regards > > > -- Bastien > > > On Oct 23, 12:37 pm, Matt Wynne <m... at mattwynne.net> wrote: > >> On 23 Oct 2008, at 10:16, Bastien wrote: > > >> > Hi everyone, > >> > Do anyone know if there''s a way to spec protected methods ? > >> > Thanks in advance > > >> Sorry if this sounds preachy, but I would always advise you to factor > >> out the code in the method into another class, then test that class. > > >> If the behaviour of the method is so complex you need to test it on > >> it''s own, it deserves to be in a class all by itself. > > >> This is what we call, ''listening to your specs'' - if it''s hard to > >> spec, you''ve probably got the design wrong. > > >> HTH, > >> Matt > >> _______________________________________________ > >> rspec-users mailing list > >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > > rspec-users mailing list > > rspec-us... at rubyforge.org > >http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
On Thu, Oct 23, 2008 at 10:00 AM, Bastien <bastien.vaucher at gmail.com> wrote:> Thanks for all your answers. > > I was just wondering how about using send in my specs ? It would > bypass the protected limitation without altering my code : > > @survey.send( :sub_total, [@mock_report] ) > > Is that the kind of trickery you were referring to David ? Would that > be bad practice ?If you''re going to do this, use __send__ or your code examples will break if/when you go to Ruby >= 1.9. As for good/bad practice, here''s the way I see it: there is a lot of evidence that encapsulation is a good thing in that it constrains contact points between objects, making them less coupled and easier to modify internally, swap in and out (polymorphism), etc, etc. Every time you violate encapsulation by using __send__, even in code examples, you increase coupling by a little bit, and make your system that much more brittle. So while one violation might seem harmless, it''s a slippery slope (sorry for the clich?). With all that - you''ll find __send__ in my code, and in my code examples. But I''m conscious of the risks. FWIW. Cheers, David> > Regards > > -- Bastien > > On Oct 23, 3:51 pm, "David Chelimsky" <dchelim... at gmail.com> wrote: >> On Thu, Oct 23, 2008 at 6:49 AM, Bastien <bastien.vauc... at gmail.com> wrote: >> > I totally agree with the ''''listening to your specs" concepts and >> > always divide my code into small methods easier to spec. >> > Now let me get you right, here''s my code : >> >> > class Survey < ActiveRecord::Base >> > has_many :participants >> > ... >> > def generate_reports >> > ... >> > sub_total = sub_total(participants.reports) >> > ... >> > end >> >> > protected >> >> > def sub_total(reports) >> > sub_total = 0 >> > reports.collect do |report| >> > sub_total = sub_total + report.score >> > end >> > return sub_total >> > end >> >> > end >> >> > I m already specing the "generate_reports" methods with a mock on >> > "sub_total", but I would also like to spec that method. I''ve put this >> > method into protected because it doesn''t make sens to call it from >> > outside this class. But you''re arguing that I should put that method >> > into a lib ? I m not sure it would make sense as this method is very >> > specific to that class, and would actually be easy to spec. Except >> > that it''s not possible to call a protected method from a spec as far >> > as I know, correct me if I m wrong. (I maybe didn''t explain my problem >> > well in the first place) >> >> This sort of decision is always a balancing act. Leaving it where it >> is means you have to either >> >> * unprotect it, which you clearly prefer not to do >> * expose it for you code example using clever ruby trickery >> * give up testing it directly >> * move it to another class, adding conceptual weight to your system >> >> None of these are perfect and all come with some tradeoff. >> >> I''ll confess that, in practice, I''d probably not test this directly. >> More often than not, for me, the sub_total method is a result of >> refactoring the generate_reports method, which by that time would have >> at least two code examples in which the only difference is the sum of >> scores of the reports. >> >> In this case, however, assuming participants and reports are AR backed >> models, you don''t really need to introduce a new class: >> >> subtotal = participants.reports.sum(''score'') >> >> Cheers, >> David >> >> >> >> >> >> > Regards >> >> > -- Bastien >> >> > On Oct 23, 12:37 pm, Matt Wynne <m... at mattwynne.net> wrote: >> >> On 23 Oct 2008, at 10:16, Bastien wrote: >> >> >> > Hi everyone, >> >> > Do anyone know if there''s a way to spec protected methods ? >> >> > Thanks in advance >> >> >> Sorry if this sounds preachy, but I would always advise you to factor >> >> out the code in the method into another class, then test that class. >> >> >> If the behaviour of the method is so complex you need to test it on >> >> it''s own, it deserves to be in a class all by itself. >> >> >> This is what we call, ''listening to your specs'' - if it''s hard to >> >> spec, you''ve probably got the design wrong. >> >> >> HTH, >> >> Matt >> >> _______________________________________________ >> >> rspec-users mailing list >> >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users >> > _______________________________________________ >> > rspec-users mailing list >> > rspec-us... at rubyforge.org >> >http://rubyforge.org/mailman/listinfo/rspec-users >> >> _______________________________________________ >> rspec-users mailing list >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
It appears we were too clever, but I think this would be a reasonable use of stories. We tried to have a story file run with 2 different sets of steps to use the same story against both the UI using selenium and against the server API using regular get/put/post. This way we could spec the server API and the UI while ensuring they are in sync for the scenario in question. But stories get confused when there are stories or scenarios with the same names. Does Cucumber deal with this any better than the story runner? Given that it searches for steps rather than having them declared, I would think you can not share story files for different steps? Michael
On Thu, Oct 23, 2008 at 7:20 PM, Michael Latta <lattam at mac.com> wrote:> It appears we were too clever, but I think this would be a reasonable use of > stories. > > We tried to have a story file run with 2 different sets of steps to use the > same story against both the UI using selenium and against the server API > using regular get/put/post. This way we could spec the server API and the > UI while ensuring they are in sync for the scenario in question. But > stories get confused when there are stories or scenarios with the same > names. > > Does Cucumber deal with this any better than the story runner? Given that > it searches for steps rather than having them declared, I would think you > can not share story files for different steps? >I''ve done this on a couple of projects. steps/common/*_steps.rb steps/watir/*_steps.rb steps/rails/*_steps.rb Set up two tasks and use --require. Or use --profile. See http://github.com/aslakhellesoy/cucumber/wikis/migration-from-rspec-stories for details. Aslak> Michael > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
On Thu, Oct 23, 2008 at 1:20 PM, Michael Latta <lattam at mac.com> wrote:> It appears we were too clever, but I think this would be a reasonable use of > stories. > > We tried to have a story file run with 2 different sets of steps to use the > same story against both the UI using selenium and against the server API > using regular get/put/post. This way we could spec the server API and the > UI while ensuring they are in sync for the scenario in question. But > stories get confused when there are stories or scenarios with the same > names. > > Does Cucumber deal with this any better than the story runner? Given that > it searches for steps rather than having them declared, I would think you > can not share story files for different steps?You can do this, you''d just put the steps in different directories and tell cucumber to require the files in one directory for one run, and another for another run. ie: cucumber --require features/steps/selenium/ features/ AND cucumber --require features/steps/normal/ features/ -- Zach Dennis http://www.continuousthinking.com http://www.mutuallyhuman.com
On Thu, Oct 23, 2008 at 5:16 AM, Bastien <bastien.vaucher at gmail.com> wrote:> Hi everyone, > Do anyone know if there''s a way to spec protected methods ? > Thanks in advanceFWIW, I blogged my answer to this question recently: http://avdi.org/devblog/2008/10/21/testing-private-methods/ -- Avdi Home: http://avdi.org Developer Blog: http://avdi.org/devblog/ Twitter: http://twitter.com/avdi Journal: http://avdi.livejournal.com
On Thu, Oct 23, 2008 at 5:00 PM, Avdi Grimm <avdi at avdi.org> wrote:> FWIW, I blogged my answer to this question recently: > http://avdi.org/devblog/2008/10/21/testing-private-methods/...and to be a little more concrete, at first glance the way I''d apply that advice to your code example is to make a class that either subclasses or wraps (via DelegateClass/SimpleDelegate) Array and adds a #sub_total method, something like the following: class Survey < ActiveRecord::Base has_many :participants ... def generate_reports ... sub_total = ReportCollection.new(participants.reports).sub_total ... end class ReportCollection < SimpleDelegator def sub_total sub_total = 0 self.collect do |report| sub_total = sub_total + report.score end return sub_total end end end -- Avdi Home: http://avdi.org Developer Blog: http://avdi.org/devblog/ Twitter: http://twitter.com/avdi Journal: http://avdi.livejournal.com
That works when running one at a time. When I want to run all my specs how do I ensure the requires are applied to the appropriate features? It is not all my stories, only a subset that are dual- purposed. Do I need to have directories for those that can be run dual mode and those that are not, and then have a script that runs the 3 cases? Michael On Oct 23, 2008, at 11:49 AM, Zach Dennis wrote:> On Thu, Oct 23, 2008 at 1:20 PM, Michael Latta <lattam at mac.com> wrote: >> It appears we were too clever, but I think this would be a >> reasonable use of >> stories. >> >> We tried to have a story file run with 2 different sets of steps to >> use the >> same story against both the UI using selenium and against the >> server API >> using regular get/put/post. This way we could spec the server API >> and the >> UI while ensuring they are in sync for the scenario in question. But >> stories get confused when there are stories or scenarios with the >> same >> names. >> >> Does Cucumber deal with this any better than the story runner? >> Given that >> it searches for steps rather than having them declared, I would >> think you >> can not share story files for different steps? > > You can do this, you''d just put the steps in different directories and > tell cucumber to require the files in one directory for one run, and > another for another run. ie: > > cucumber --require features/steps/selenium/ features/ > AND > cucumber --require features/steps/normal/ features/ > > -- > Zach Dennis > http://www.continuousthinking.com > http://www.mutuallyhuman.com > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
On Thu, Oct 23, 2008 at 11:20 PM, Michael Latta <lattam at mac.com> wrote:> That works when running one at a time. When I want to run all my specs how > do I ensure the requires are applied to the appropriate features? It is not > all my stories, only a subset that are dual-purposed. Do I need to have > directories for those that can be run dual mode and those that are not, and > then have a script that runs the 3 cases? >You have to run once for each "environment". So if you have say selenium and rails, you''ll have to run all features that use selenium in one run and those that use rails in another. (Of course you can have overlap - some features that include run in both runs) Aslak> Michael > > > On Oct 23, 2008, at 11:49 AM, Zach Dennis wrote: > >> On Thu, Oct 23, 2008 at 1:20 PM, Michael Latta <lattam at mac.com> wrote: >>> >>> It appears we were too clever, but I think this would be a reasonable use >>> of >>> stories. >>> >>> We tried to have a story file run with 2 different sets of steps to use >>> the >>> same story against both the UI using selenium and against the server API >>> using regular get/put/post. This way we could spec the server API and >>> the >>> UI while ensuring they are in sync for the scenario in question. But >>> stories get confused when there are stories or scenarios with the same >>> names. >>> >>> Does Cucumber deal with this any better than the story runner? Given >>> that >>> it searches for steps rather than having them declared, I would think you >>> can not share story files for different steps? >> >> You can do this, you''d just put the steps in different directories and >> tell cucumber to require the files in one directory for one run, and >> another for another run. ie: >> >> cucumber --require features/steps/selenium/ features/ >> AND >> cucumber --require features/steps/normal/ features/ >> >> -- >> Zach Dennis >> http://www.continuousthinking.com >> http://www.mutuallyhuman.com >> _______________________________________________ >> 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 >