On Mon, Jan 4, 2010 at 7:06 AM, Ijonas Kisselbach < ijonas.kisselb...@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 <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 >
_______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users