[rspec-users] Data-wise context combination for controller speccing

David Chelimsky dchelimsky at gmail.com
Mon Jul 19 06:55:57 EDT 2010


On Jul 19, 2010, at 3:58 AM, Matt Wynne wrote:

> 
> On 18 Jul 2010, at 00:10, David Chelimsky wrote:
> 
>> On Jul 17, 2010, at 1:18 PM, Costa Shapiro wrote:
>> 
>>> Hello,
>>> 
>>> I've been thinking of how to express my idea in code, but since I've never been involved in RSpec development, I'd better have some feedback here first.
>>> The feature suggestion below applies to any controller-like code under spec, i.e. a stateless module producing output or just altering its data store (it doesn't necessarily have to be a C of the MVC, but I suppose merb/rails developers will particularly appreciate it).
>>> 
>>> Here is a skimmed sample to illustrate the pain:
>>> 
>>>     describe BookController do
>>>     
>>>       context "registering a book" do
>>>     
>>>         specify "from a new author on a new subject" do
>>>           auth = mock(:name => 'John Doe')
>>>           Author.should_receive(:find_
>>> by_name).and_return(nil)
>>>           Author.should_receive(:new).and_return(auth)
>>>           auth.should_receive(:save).and_return(true)
>>>     
>>>           subj = mock(:short => 'Mockery')
>>>           Subject.should_receive(:find_by_short).and_return(nil)
>>>           Subject.should_receive(:new).and_return(subj)
>>>           subj.should_receive(:save).and_return(true)
>>>     
>>>           title = 'Specs on Steroids'
>>>     
>>>           book = mock
>>>           Book.should_receive(:new).and_return(book)
>>>           book.should_receive(:save).and_return(true)
>>>     
>>>           post :register :author => auth.name, :title => title, :subject => subj.short
>>>           response.should be_success
>>>         end
>>>     
>>>         specify "from a known author on a new subject" do
>>>           auth = mock(:name => 'Joe Dohn')
>>>           Author.should_receive(:find_by_name).and_return(auth)
>>>     
>>>           subj = mock(:short => 'Mockery')
>>>           Subject.should_receive(:find_by_short).and_return(nil)
>>>           Subject.should_receive(:new).and_return(subj)
>>>           subj.should_receive(:save).and_return(true)
>>>     
>>>           title = 'Specs on Steroids II'
>>>     
>>>           book = mock
>>>           Book.should_receive(:new).and_return(book)
>>>           book.should_receive(:save).and_return(true)
>>>     
>>>           post :register :author => auth.name, :title => title, :subject => subj.short
>>>           response.should be_success
>>>         end
>>>     
>>>         specify "from a known author on a known subject" do
>>>           auth = mock(:name => 'Joe Dohn')
>>>           Author.should_receive(:find_by_name).and_return(auth)
>>>     
>>>           subj = mock(:short => 'Forgery')
>>>           Subject.should_receive(:find_by_short).and_return(subj)
>>>     
>>>           #...
>>>         end
>>>     
>>>         specify "from a new author on a known subject" do
>>>           #...
>>>         end
>>>       end
>>>     end
>>> 
>>> 
>>> And this is what I have in mind for doing exactly the same job:
>>> 
>>>     describe BookController do
>>>     
>>>       context "registering a book" do
>>>     
>>>         before :any, "from a new author", :author do
>>>           @auth = mock(:name => 'John Doe')
>>>           Author.should_receive(:find_by_name).and_return(nil)
>>>           Author.should_receive(:new).and_return(@auth)
>>>           @auth.should_receive(:save).and_return(true)
>>>         end
>>>     
>>>         before :any, "from a known author", :author do
>>>           @auth = mock(:name => 'Joe Dohn')
>>>           Author.should_receive(:find_by_name).and_return(@auth)
>>>         end
>>>     
>>>         before :any, "on a new subject", :subject do
>>>           @subj = mock(:short => 'Mockery')
>>>           Subject.should_receive(:find_by_short).and_return(nil)
>>>           Subject.should_receive(:new).and_return(@subj)
>>>           @subj.should_receive(:save).and_return(true)
>>>         end
>>>     
>>>         before :any, "on a known subject", :subject do
>>>             @subj = mock(:name => 'Joe Dohn')
>>>             Subject.should_receive(:find_by_name).and_return(@subj)
>>>         end
>>>     
>>>         it "should succeed", :with => [:author, :subject] do
>>>           title = 'Specs on Steroids X'
>>>     
>>>           post :register :author => @auth.name, :title => title, :subject => @subj.short
>>>           response.should be_success
>>>         end
>>>       end
>>>     end
>>> 
>>> A run of such specs will effectively multiply the tests — automatically — choosing before and after blocks as specified.
>>> I'm sorry, I haven't thought the DSL through, but I hope the main idea can be seen: contexts do not have to be hierarchical.
>>> In my opinion, adding some sort of context selection+combination capabilities (AOP-style) will contribute greatly to the expressiveness of the spec language.
>> 
>> I think the idea of mixing/matching sub-contexts is very interesting, but it definitely needs from fleshing out. It would have to be easy to read/understand in the spec file as well as the output.
>> 
>> Also, this only works if every combination should behave the same way. I think we'd need a means of saying "given these combinations of data, expect these outcomes".
>> 
>> Anybody else have thoughts on this?
> 
> It's a nice idea.
> 
> I'm not sure whether I'd use it though. I think this idea comes from the desire to write specs that are *complete*, which I can perfectly understand but I don't think I subscribe to anymore. I prefer to really craft the examples so there's 'just enough' tests but no more than that. I'd be worried this might offer a temptation to think less about why you're writing each example, and I'd be worried how that would help me to do TDD.

Agreed this would be a potential pitfall of this approach. I'm also not convinced this is the right tool for this sort of thing. Seems like something best expressed in a table:

data do
  author do
    new { ... }
    known { ... }
  end

  subject do
    new { ... }
    known { ... }
  end

  outcome do
    success { ... }
  end
end

scenarios <<-SCENARIOS
 | author | subject | outcome |
 | new    | new     | success |
 | known  | new     | success |
 | new    | known   | success |
 | known  | known   | success |
SCENARIOS

Something like this might work, but maybe it's better suited for Cucumber, or FIT.

> It should be possible to do something like this using macros now, right? Can I suggest that the OP has a go at refactoring his code using macros and we can see how it looks?
> 
>> 
>> Cheers,
>> David
>> 
>> 
>>> Thank you for your attention,
>>> Costa.
>> 
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
> 
> cheers,
> Matt
> 
> http://blog.mattwynne.net
> +44(0)7974 430184
> 
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100719/e646bc0d/attachment-0001.html>


More information about the rspec-users mailing list