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. Thanks for the help. On Mon, Jan 4, 2010 at 12:56 PM, David Chelimsky <dchelim...@gmail.com>wrote: > On Mon, Jan 4, 2010 at 5:26 AM, Ijonas Kisselbach < > ijonas.kisselb...@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 <dchelim...@gmail.com>wrote: >> >>> On Mon, Jan 4, 2010 at 4:33 AM, Ijonas Kisselbach < >>> ijonas.kisselb...@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@rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >>> >> >> >> _______________________________________________ >> rspec-users mailing list >> rspec-users@rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> > > > _______________________________________________ > rspec-users mailing list > rspec-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
_______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users