[rspec-users] Question about structuring specs

David Chelimsky dchelimsky at gmail.com
Mon Jan 4 08:08:30 EST 2010


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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/900ecc2b/attachment-0001.html>


More information about the rspec-users mailing list