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''m using rspec 1.2.9 and Rails 2.2.2 on OSX) Regards, Ijonas. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/4a547f08/attachment.html>
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.-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/c4d5ccbd/attachment.html>
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. 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 >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/b87d80b3/attachment.html>
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 >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/3cfeb4f2/attachment-0001.html>
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 <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 >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20100104/e3095d81/attachment.html>
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>
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>