Question about structuring specs

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.

On Mon, Jan 4, 2010 at 4:33 AM, Ijonas Kisselbach <
[email protected]> wrote:

context ", when new policies are activated, but content remains the
context “, when new policies are activated, but content remains the same”
# a whole bunch of expections testing retrieval of previous violations
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

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 5:26 AM, Ijonas Kisselbach <
[email protected]> wrote:

end

ContentDescriptor.should_receive(:by_path_md5_and_site).once.and_return([@content_descriptor])

  @content.should_receive(:most_recent_violations=).once

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,

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 7:06 AM, Ijonas Kisselbach <
[email protected]> 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

Definitely helpful. Thanks, its much appreciated.