[rspec-users] Question about structuring specs

Ijonas Kisselbach ijonas.kisselbach at gmail.com
Mon Jan 4 08:21:22 EST 2010


Definitely helpful. Thanks, its much appreciated.

On Mon, Jan 4, 2010 at 1:08 PM, David Chelimsky <dchelimsky at gmail.com>wrote:

> On Mon, Jan 4, 2010 at 7:06 AM, Ijonas Kisselbach <
> ijonas.kisselbach at gmail.com> wrote:
>
>> Hi David,
>>
>> Thanks for the suggestions... Yes I am mocking to avoid DB access, but if
>> the cost is DB access vs. readability: I'll choose readability.
>>
>> Would you use something like Factory Girl or Machinist to setup the DB? To
>> avoid too much mocking and to avoid old-skool fixtures.
>>
>
> YES!
>
> Thanks for the help.
>>
>
> Assuming it was helpful, you're welcome.
>
> Cheers,
> David
>
>
>>
>> On Mon, Jan 4, 2010 at 12:56 PM, David Chelimsky <dchelimsky at gmail.com>wrote:
>>
>>> On Mon, Jan 4, 2010 at 5:26 AM, Ijonas Kisselbach <
>>> ijonas.kisselbach at gmail.com> wrote:
>>>
>>>> Hi David,
>>>>
>>>> I see your point about concentrating on outcomes rather than
>>>> implementation. I suppose who cares about implementation, its the effect
>>>> code on the "state of the world" that is important.
>>>>
>>>> Here's an unmodified sample:
>>>>   context ", when a new policy is activated on unchanged content" do
>>>>     before(:each) do
>>>>       setup_common_mocks
>>>>     end
>>>>
>>>>     it "should create a new policy, the new violation and location,
>>>> resolve previous violations, recalculate location MD5, and refresh caches"
>>>> do
>>>>       # content hasn't changed
>>>>       @content = mock(:content, :content_md5 => "84290324908230948",
>>>> :most_recent_violations => [mock(:violation_mr, :update_folder_count =>
>>>> nil)])
>>>>       @content_descriptor = mock(:content_descriptor, :contents =>
>>>> [@content], :most_recent_content => @content, :folder => mock(:folder))
>>>>
>>>> ContentDescriptor.should_receive(:by_path_md5_and_site).once.and_return([@content_descriptor])
>>>>
>>>>
>>>>       # Policy is new, gets created once
>>>>       Policy.should_receive(:find_by_account_category_and_name).once
>>>>
>>>> PolicyCategory.should_receive(:find_by_account_and_name).once.and_return(mock(:policy_category))
>>>>       Policy.should_receive(:create!).once.and_return(mock(:policy))
>>>>
>>>>       # below are the changes affected
>>>>       Violation.should_receive(:resolve_violations).once
>>>>
>>>> Violation.should_receive(:recalculate_violation_md5).once.and_return(10)
>>>>
>>>> Violation.should_receive(:find_by_content_id_and_policy_id).once.and_return(nil)
>>>>       violation = mock(:new_violation1, :location_md5= => nil, :save =>
>>>> true)
>>>>       Violation.should_receive(:new).once.and_return(violation)
>>>>       violation.should_receive(:save).once
>>>>
>>>>
>>>> Location.should_receive(:create!).once.and_return(mock(:location1))
>>>>
>>>>       @content.should_receive(:most_recent_violations=).once
>>>>       @content.should_receive(:unresolved_violation_count=).once
>>>>       @content.should_receive(:save).once
>>>>       CacheMaintenance.should_receive(:remove_folder_cache_keys).twice
>>>>       record
>>>>     end
>>>>
>>>>   end
>>>>
>>>> I'm going to try and refactor the specs so that anything that doesn't
>>>> directly modify state of the app is removed.
>>>>
>>>
>>> Why are you mocking so much here? Why not set up the db in a known state,
>>> invoke the action you want to invoke (record???) and set expectations about
>>> the outcomes? If you're concerned about database access and speed, this is a
>>> case where I think the benefits of just invoking the code outweighs the cost
>>> of database access. I'm imagining something more like this:
>>>
>>> context ", when a new policy is activated on unchanged content" do
>>>   it "creates a new policy" do
>>>     record
>>>     # expect to find the policy by known attributes
>>>     # something like this:
>>>     #   Policy.find_by_account_category_and_name(....).should_not be_nil
>>>   end
>>>
>>>   it "creates a new violation" do
>>>     record
>>>     # same as the policy - find the Violation by known attributes
>>>   end
>>>
>>>   it "updates most_recent_violations" do
>>>     record
>>>     # query for most recent violations and expect the violation
>>>     # to be found
>>>   end
>>>
>>>   it "updates the violation count" do
>>>     expect { record }.to change
>>> {content.unresolved_violation_count}.by(1)
>>>   end
>>>
>>>   # etc, etc
>>> end
>>>
>>> Yes, this means that the process needs to happen more than once, but each
>>> example becomes much easier to grok.
>>>
>>> WDYT?
>>>
>>> Thanks,
>>>> Ijonas.
>>>>
>>>>  On Mon, Jan 4, 2010 at 11:16 AM, David Chelimsky <dchelimsky at gmail.com
>>>> > wrote:
>>>>
>>>>>  On Mon, Jan 4, 2010 at 4:33 AM, Ijonas Kisselbach <
>>>>> ijonas.kisselbach at gmail.com> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> I'm struggling with structuring my specs describing a large process in
>>>>>> my app. There are multiple paths of execution through that process each of
>>>>>> which I'm trying to describe using a different rspec context, eg.
>>>>>>
>>>>>> describe Violation do
>>>>>>    context ", when nothing has changed since the last run"
>>>>>>    context ", when new content has been created, but policies remain
>>>>>> the same"
>>>>>>    context ", when new policies are activated, but content remains the
>>>>>> same"
>>>>>> end
>>>>>>
>>>>>> Each of the three scenarios/context above have got a bunch of "it
>>>>>> should..." blocks in it which in turn contain a whole bunch of
>>>>>> should_receives and should_not_receives on various mocked objects, thereby
>>>>>> exercising the functionality of the large process.
>>>>>>
>>>>>> I would like the context to read as follows:
>>>>>>
>>>>>> context ", when new policies are activated, but content remains the
>>>>>> same" do
>>>>>>    it "should create the new policy" do
>>>>>>       # a whole bunch of expectations testing the policy creation part
>>>>>> of the process
>>>>>>    end
>>>>>>    it "should create a new violation and location" do
>>>>>>      # a whole bunch of expectations testing the violation creation
>>>>>> part of the process
>>>>>>    end
>>>>>>    it "should resolve previous violations" do
>>>>>>      # a whole bunch of expections testing retrieval of previous
>>>>>> violations and performing updates on them
>>>>>>    end
>>>>>>   ....
>>>>>> end
>>>>>>
>>>>>> The problem is: if I compartmentalize my expectations into the
>>>>>> individual it-should-blocks then something will fail in the execution of the
>>>>>> large process, typically caused by a mock not being setup. If I lump all my
>>>>>> expectations in the before(:each)-block then the whole thing springs to
>>>>>> life, but I lose my compartmentalization of the specs and the whole thing
>>>>>> becomes unreadable.
>>>>>>
>>>>>> I guess I'm looking for help and advice on how best combat the lumping
>>>>>> of expectations into the before-block. Should I separate my stubbing from my
>>>>>> expectations ?
>>>>>>
>>>>>> Many thanks for your advice.
>>>>>>
>>>>>
>>>>> I'd need to see the actual code to respond in any precise way here, but
>>>>> generally, it sounds like you're specifying too much about the
>>>>> implementation rather than the outcomes. What happens if you eliminate all
>>>>> of the mocks in these examples and just have expectations like
>>>>> "Policy.find(@policy_id).should_not be_nil"?
>>>>>
>>>>> David
>>>>>
>>>>>
>>>>>>
>>>>>> (I'm using rspec 1.2.9 and Rails 2.2.2 on OSX)
>>>>>>
>>>>>> Regards,
>>>>>> Ijonas.
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> 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
>>>
>>
>>
>> _______________________________________________
>> 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/351820fe/attachment.html>


More information about the rspec-users mailing list