Tobias Grimm
2007-Jan-04 23:03 UTC
[rspec-users] Common setup code and naming specifications
Hello! I have a lot of contexts for testing Rails controllers, that must do something like ''session[:logged_in] = true'' in their setup. How can this be refactored? In unit tests I would simply create a LoggedInControllerTest base class, that all my functional tests would derive from. And another small question: In my controller specifications I often have to decide whether to write: specify "should provide the first ten items in @items and three pages in @pages when passing no :page parameter to the :list action" or specify "should provide the first ten items and three pages when not selecting a specific page" So the decision is, whether to explicitly name parameters, return values and actions or to use a more abstract phrase. How do you handle this? Sorry, might be a stupid question, but I''m, still trying to get the right "feeling" for BDD-style testing. bye, Tobias
David Chelimsky
2007-Jan-04 23:12 UTC
[rspec-users] Common setup code and naming specifications
On 1/4/07, Tobias Grimm <listaccount at e-tobi.net> wrote:> Hello! > > I have a lot of contexts for testing Rails controllers, that must do > something like ''session[:logged_in] = true'' in their setup. How can this > be refactored? In unit tests I would simply create a > LoggedInControllerTest base class, that all my functional tests would > derive from.module MyHelpers def set_logged_in session[:logged_in] = true end end context "..." do include MyHelpers setup do set_logged_in end end> > And another small question: > > In my controller specifications I often have to decide whether to write: > > specify "should provide the first ten items in @items and three pages in > @pages when passing no :page parameter to the :list action" > > or > > specify "should provide the first ten items and three pages when not > selecting a specific page"Definitely the latter. These names should be how the customer would talk about requirements, not how developers would - UNLESS you''re writing a framework for developers and developers ARE your customers. David> So the decision is, whether to explicitly name parameters, return values > and actions or to use a more abstract phrase. How do you handle this? > Sorry, might be a stupid question, but I''m, still trying to get the > right "feeling" for BDD-style testing. > > bye, > > Tobias > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Bob Cotton
2007-Jan-04 23:34 UTC
[rspec-users] Common setup code and naming specifications
"David Chelimsky" <dchelimsky at gmail.com> writes:> On 1/4/07, Tobias Grimm <listaccount at e-tobi.net> wrote: >> Hello! >> >> I have a lot of contexts for testing Rails controllers, that must do >> something like ''session[:logged_in] = true'' in their setup. How can this >> be refactored? In unit tests I would simply create a >> LoggedInControllerTest base class, that all my functional tests would >> derive from. > > module MyHelpers > def set_logged_in > session[:logged_in] = true > end > end > > context "..." do > include MyHelpers > setup do > set_logged_in > end > end >What about: module LoggedIn def setup session[:logged_in] = true end end context "..." do include LoggedIn end A little less typing, and just as clear. -Bob>> >> And another small question: >> >> In my controller specifications I often have to decide whether to write: >> >> specify "should provide the first ten items in @items and three pages in >> @pages when passing no :page parameter to the :list action" >> >> or >> >> specify "should provide the first ten items and three pages when not >> selecting a specific page" > > Definitely the latter. These names should be how the customer would > talk about requirements, not how developers would - UNLESS you''re > writing a framework for developers and developers ARE your customers. > > David > >> So the decision is, whether to explicitly name parameters, return values >> and actions or to use a more abstract phrase. How do you handle this? >> Sorry, might be a stupid question, but I''m, still trying to get the >> right "feeling" for BDD-style testing. >> >> bye, >> >> Tobias >> >> _______________________________________________ >> 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-- Bob Cotton Test Architect -- Rally Software -- rallydev.com http://www.testarchitecture.com/blog
Tobias Grimm
2007-Jan-05 00:15 UTC
[rspec-users] Common setup code and naming specifications
Thanks for your fast response! David Chelimsky wrote:> context "..." do > include MyHelpers > setup do > set_logged_in > end > end >Ok, but this still requires me to call set_logged_in in 99% of my controller contexts. I can live with it, but it tastes like evil duplication.>> specify "should provide the first ten items in @items and three pages in >> @pages when passing no :page parameter to the :list action" >> >> specify "should provide the first ten items and three pages when not >> selecting a specific page" >> > > Definitely the latter. These names should be how the customer would > talk about requirements, not how developers would - UNLESS you''re > writing a framework for developers and developers ARE your customers. >Fine - sounds reasonable! But sometimes it''s hard to see, whether you are working on a framework or not. If I''m at a controller, it''s not only the customer who uses it in some way, it''s also the view. And the view needs to call :list and pass :page and expects to get @items and @pages. The second of the two specifications above tells nothing about how the view should interact with the controller. Tobias
Bryan Liles
2007-Jan-05 00:16 UTC
[rspec-users] Common setup code and naming specifications
On Jan 4, 2007, at 6:34 PM, Bob Cotton wrote:> > What about: > > module LoggedIn > def setup > session[:logged_in] = true > end > end > > context "..." do > include LoggedIn > end >With this method, don''t you lose access to setup? Seems like a lot to lose just to gain a a login mixin.
David Chelimsky
2007-Jan-05 03:05 UTC
[rspec-users] Common setup code and naming specifications
On 1/4/07, Tobias Grimm <listaccount at e-tobi.net> wrote:> Thanks for your fast response! > > David Chelimsky wrote: > > context "..." do > > include MyHelpers > > setup do > > set_logged_in > > end > > end > > > > Ok, but this still requires me to call set_logged_in in 99% of my > controller contexts. I can live with it, but it tastes like evil > duplication.Ah, the DRY battle cry. There is a difference between duplication of code and duplicated calls to a no-arg method. While typing the call to set_logged_in may require a few extra strokes, the meaning of it will never change in different contexts. So the risks associated w/ code duplication are mitigated. In fact, the real risk is that you might change its one and only meaning, in which case you''d have to look at each case and decide if the new meaning makes sense. That problem wouldn''t go away if you could subclass contexts. In fact, it would be more sinister because the behaviour is implicit. At least if you look at set_logged_in in each context you have some sense of what it means. There is also a difference between duplication in code and duplication in tests. Jay Fields put it very well when he put it this way: test are inherently procedural. The risk of duplication in code is that when you have to change it in one place you''ll miss the other place. That risk doesn''t really apply to this situation. Alternatively, you could figure out a way to subclass contexts and not be able to look at a given context and understand why the user keeps on ending up logged in. My only slightly solicited 2 cents.> > >> specify "should provide the first ten items in @items and three pages in > >> @pages when passing no :page parameter to the :list action" > >> > >> specify "should provide the first ten items and three pages when not > >> selecting a specific page" > >> > > > > Definitely the latter. These names should be how the customer would > > talk about requirements, not how developers would - UNLESS you''re > > writing a framework for developers and developers ARE your customers. > > > > Fine - sounds reasonable! But sometimes it''s hard to see, whether you > are working on a framework or not. If I''m at a controller, it''s not only > the customer who uses it in some way, it''s also the view. And the view > needs to call :list and pass :page and expects to get @items and @pages. > The second of the two specifications above tells nothing about how the > view should interact with the controller.Excellent point. There''s no simple answer for this. Your best bet is to keep throwing examples at us and we can discuss them more explicitly. Cheers, David> Tobias > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Bob Cotton
2007-Jan-05 03:42 UTC
[rspec-users] Common setup code and naming specifications
Bryan Liles <bryan at osesm.com> writes:> On Jan 4, 2007, at 6:34 PM, Bob Cotton wrote: > >> >> What about: >> >> module LoggedIn >> def setup >> session[:logged_in] = true >> end >> end >> >> context "..." do >> include LoggedIn >> end >> > > With this method, don''t you lose access to setup? Seems like a lot > to lose just to gain a a login mixin.Sorry, LoggedIn should have been a class: class One def setup puts "in setup one" end end context "context with inherit" do inherit One setup do puts "in context setup" end specify "some specify" do puts "in specify" end end $ spec foo_spec..rb in setup one in context setup in specify . I do agree with David, that some duplication in test code is ok. It would be easy to get the LoggedIn class buried up the inheritance hierarchy where it can''t be seen. But if it''s the only superclass of the context, and it''s visible, and it named correctly, this will save you some typing. -Bob -- Bob Cotton Test Architect -- Rally Software -- rallydev.com http://www.testarchitecture.com/blog
David Chelimsky
2007-Jan-05 03:50 UTC
[rspec-users] Common setup code and naming specifications
On 1/4/07, Bob Cotton <bob.cotton at rallydev.com> wrote:> Bryan Liles <bryan at osesm.com> writes: > > > On Jan 4, 2007, at 6:34 PM, Bob Cotton wrote: > > > >> > >> What about: > >> > >> module LoggedIn > >> def setup > >> session[:logged_in] = true > >> end > >> end > >> > >> context "..." do > >> include LoggedIn > >> end > >> > > > > With this method, don''t you lose access to setup? Seems like a lot > > to lose just to gain a a login mixin. > > Sorry, LoggedIn should have been a class: > > class One > def setup > puts "in setup one" > end > end > > context "context with inherit" do > inherit One > > setup do > puts "in context setup" > end > > specify "some specify" do > puts "in specify" > end > end > > $ spec foo_spec..rb > > in setup one > in context setup > in specify > . > > > I do agree with David, that some duplication in test code is ok. It > would be easy to get the LoggedIn class buried up the inheritance > hierarchy where it can''t be seen. > > But if it''s the only superclass of the context, and it''s visible, and > it named correctly, this will save you some typing.Great idea - but unfortunately we already use "inherit" in all of the rails contexts. That''s ultimately how we''re able to tap into Test::Unit::TestCase to take advantage of fixture loading, etc.> > -Bob > > -- > Bob Cotton > Test Architect -- Rally Software -- rallydev.com > http://www.testarchitecture.com/blog > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Tobias Grimm
2007-Jan-05 09:25 UTC
[rspec-users] Common setup code and naming specifications
David Chelimsky wrote:> Ah, the DRY battle cry. >Yeeehaaa! :)> There is a difference between duplication of code and duplicated calls > to a no-arg method. While typing the call to set_logged_in may require > a few extra strokes,You''re right. At least in this particular case there would be no gain in having something like a logged_in_context-"base class". It wouldn''t save very much keystrokes and it doesn''t communicate better than putting set_logged_in in the setup. If there''s common setup code, it should be refactored into a helper module and included in the context.> are inherently procedural. The risk of duplication in code is that > when you have to change it in one place you''ll miss the other place. > That risk doesn''t really apply to this situation. >The only thing I''m constantly missing is putting the logged in code in the setup, when creating a new context. But a subclassing approach probably wouldn''t make this any better. Maybe I just shouldn''t code at one o''clock in the night :-)>> Excellent point. There''s no simple answer for this. Your best bet is >> to keep throwing examples at us and we can discuss them more >> explicitly. >>>From another point of view, one might also see it this way:Not putting implementation details into the specification title gives you more freedom to implement it along the way. In this sense I would describe the anatomy of a specification as: specify "<title>" do <body> <usage> <verification> </body> end The <title> should be a hint on what functionality / behaviour is specified. <body> describes the details and typically consists of a <usage> part and a <verification> part. The customer does only look at the title. The developer, who e.g. implements the view, looks mainly at the <usage> part. This can also look like: setup do <usage> end specify "<title>" do <verification> end Maybe this can be seen as a pair of different languages in a translation. <title> is a phrase in the human customer language, <body> is the translation in the developer language. Mmmm... thats still not the right analogy. It''s more like a dictionary where <title> is a term in one language and <usage>/<verification> is the definition in another language. Ok, before I get too philosophical about this, I''ll rather go ahead and write some code to see where this gets me :-) Regarding the examples - what I''m currently working on is open source anyway. I''ll put it into a public SVN repository these days, so if anyone is interested he can take a look at it. bye, Tobias