Stopping example execution?

Hello, I’m wondering If I am missing something here when creating an
example that sets an expecation at the top or beginning of an action but
requires you to stub / mock everything that follows.

Example:
I want to test that a certain controller is running a
before_filter…thats easy:

  • controller.should_receive(:require_user)
  • do_get

But now i’ve got to mock / stub everything else that comes behind this
filter so that I don’t receive ‘unexpected method’ errors, or other
blowups because I am requesting the whole action. Is there anyway to
stop execution after an expectation has been met? It seems to me that
this might clean things up a bit. Not sure, I’m still fairly new to
BDD/Mocking by about 2 weeks.

Thanks

On Sat, Jun 28, 2008 at 5:57 PM, Britt Mileshosky
[email protected] wrote:

Hello, I’m wondering If I am missing something here when creating an example that sets an expecation at the top or beginning of an action but requires you to stub / mock everything that follows.

Example:
I want to test that a certain controller is running a before_filter…thats easy:

  • controller.should_receive(:require_user)
  • do_get

But now i’ve got to mock / stub everything else that comes behind this filter so that I don’t receive ‘unexpected method’ errors, or other blowups because I am requesting the whole action. Is there anyway to stop execution after an expectation has been met? It seems to me that this might clean things up a bit. Not sure, I’m still fairly new to BDD/Mocking by about 2 weeks.

Yep, you can stub out the requested action on the controller. Say
you’re testing that the :index action requires authentication:

controller.should_not_receive(:index)
stub_not_logged_in
do_get

Or the opposite:

controller.should_receive(:index)
stub_logged_in
do_get

Personally I prefer expecting the action instead of expecting the
filters, but I think both would accomplish the same goal, as long as
you tested the filter on its own. If you just wanted to stub out the
action to prevent it from doing anything, you could of course just use
controller.stub!(:index).

HTH

k


Example:
controller.should_not_receive(:index)
filters, but I think both would accomplish the same goal, as long as
http://rubyforge.org/mailman/listinfo/rspec-users
So i did something like.

  • controller.should_receive(:before_filter_action)
  • controller.stub!(:action_after_filter)
  • do_get

Works nicely… but if you think about it and look closely, we are still
stubbing methods after the intended expectation was met. This method
just happens to encapsulate a whole other set of methods, which is why
it works nicely.

But lets say i have something like this


def some_action
@user = self.current_user
@account = self.current_account if self.has_account?
@person = @account.people.find(params[:person])
end


describe “with a logged in user”

before(:each) do
controller.stub!(:current_account)
@account = stub_model(UserAccount) # Shouldn’t have to stub here?
@person = stub_model(User) # Shouldn’t have to stub
here?
@people = mock(“list of people”) # Shouldn’t have to stub
here?
@people.stub!(find) # Shouldn’t have to
stub here?
@account.stub!(:people).and_return(@people) # Shouldn’t
have to stub here?
end

it “should find current user” do
controller.should_receive(:current_user).and_return(@mockUser)
do_get
end

describe “who has an account” do
… should put account stubbing here with examples

describe “which has people” do
… should put people stubbing here with examples
end

describe “which has 0 people” do

end

end

describe “who doesnt have an account” do

end

end


Notice I have to stub everything out at the first before(:each)
declaration because my “should find current user” example will blow up
because of unexpected methods after the expectation. All I want to know
is that the current user is expected to be found and stop. My examples
LATER should define stubs and expectations.

I’d like something like

controller.should_receive(:current_user).and_return(@mockUser).end_example

Am i going about it all wrong? Is there something I’m missing or does
this just seem natural…

Gracias,
Britt



action but requires you to stub / mock everything that follows.
other blowups because I am requesting the whole action. Is there

use


@people.stub!(find) # Shouldn’t have

Scott, I don’t believe so, but can you see where that might work in the
example given above? I’ve never used the null_object flag, and looking
at the documentation, it seems as though I’d still need to declare all
my mocks at the very beginning, rather than incrementally as I work down
through my code and examples.

On Jun 28, 2008, at 8:27 PM, Britt Mileshosky wrote:

controller.should_not_receive(:index)
filters, but I think both would accomplish the same goal, as long as
[email protected]
still stubbing methods after the intended expectation was met. This
@person = @account.people.find(params[:person])
here?
@people = mock(“list of people”) # Shouldn’t have to stub
here?
@people.stub!(find) # Shouldn’t have
to stub here?
@account.stub!(:people).and_return(@people) # Shouldn’t
have to stub here?
end

Are you looking for the :null_object => true flag?

Scott

On Jun 28, 2008, at 7:27 PM, Britt Mileshosky wrote:

an example that sets an expecation at the top or beginning of an
this filter so that I don’t receive ‘unexpected method’ errors, or
do_get
action to prevent it from doing anything, you could of course just

which is why it works nicely.

here?

end

controller
.should_receive(:current_user).and_return(@mockUser).end_example

Am i going about it all wrong? Is there something I’m missing or
does this just seem natural…

You’re doing the right thing by handling the stubs before(:each) -
that’s what it’s therefore - to set up the environment so things will
run.

I would recommend avoiding instance variable assignment there,
however. If you need a variable, create it directly in the example.
This is something that I’ve only recently started doing and I find
that it keeps things much more clear.

There are a few things you can do to tidy up the stubs a bit. You
could organize things differently to express the relationships better:

before(:each) do
controller.stub!(:current_user).and_return(stub_model(User))
controller.stub!(:has_account?).and_return(true)
controller.stub!(:current_account).and_return(
stub_model(UserAccount,
:people => stub(‘people’,
:find => stub_model(Person)
)
)
)
end

If you wrap self.people.find in self.find_person in Account (which
fixes the Law of Demeter violation), like this:

def some_action
@user = self.current_user
@account = self.current_account if self.has_account?
@person = @account.find_person(params[:person])
end

… you can reduce the before to this:

before(:each) do
controller.stub!(:current_user).and_return(stub_model(User))
controller.stub!(:has_account?).and_return(true)
controller.stub!(:current_account).and_return(
stub_model(UserAccount,
:find_person => stub_model(Person)
)
)
end

In cases like current_user, you don’t really need to stub that because
the controller will just return nil.

If has_account? actually checks for current_account.nil?, then you
don’t have to stub that either. Now you just have this:

before(:each) do
controller.stub!(:current_account).and_return(
stub_model(UserAccount,
:find_person => stub_model(Person)
)
)
end

This is a bit more organized, but still quite busy. In the end, when
you’re dealing with code that violates Tell, Don’t Ask (which is quite
natural in Rails controllers), you have to stub a lot of stuff to get
the isolation you want. It’s a tradeoff.

HTH,
David


Date: Sat, 28 Jun 2008 20:32:26 -0400

To: [email protected]

I want to test that a certain controller is running a
still fairly new to BDD/Mocking by about 2 weeks.
controller.should_receive(:index)
HTH

  • controller.should_receive(:before_filter_action)

have to stub here?

I just noticed, Scott truncated my email so the full example I gave is
not shown above, please refer to the previous messages for my full
example.


To: [email protected]

I want to test that a certain controller is running a
still fairly new to BDD/Mocking by about 2 weeks.
controller.should_receive(:index)
HTH

  • controller.should_receive(:before_filter_action)

have to stub here?

up because of unexpected methods after the expectation. All I want
does this just seem natural…

   )
@person = @account.find_person(params[:person])
 )

controller.stub!(:current_account).and_return(

HTH,
David

Thank you David, that helps quite a bit, I will adapt these principles
and move on.

However, do you see where something like a return statement or end
example statement could be beneficial?
If you are working from the top down with your controller action
execution, then you only need to test your expectation
and then bail out of your action. No need to further test or meet
requirements on anything else in that action because your
single test has been met.

  • in my example for making sure I find a user, I’d like to end execution
    once I DID find the user, i shouldn’t have to satisfy
    requirements about finding an account and a person… I’ll write those
    expectations later in another nested describe group, as you
    can see here, in a top down process

PeopleController with a logged in user

  • should find user

PeopleController with a logged in user who has an account

  • should find account

PeopleController with a logged in user who doesnt have an account

  • shouldn’t find account
  • should redirect …

PeopleController with a logged in user who has an account the person
belongs to

  • should find person
  • should assign person for the view

PeopleController with a logged in user who has an account the requested
person does not belong to

  • should not find person
  • should …

and then bail out of your action. No need to further test or meet

  • should find user
  • should find person

Anybody else have opinions on that?

I understand your point, but should a testing framework be worried too
much about how a user will be writing their application code?
Leave it to the programmer to decide what logic they put in the
controller, let the testing framework make it easy to test
that certain conditions are met, no matter how those conditions are
presented in the application.

This short-circuting can be completely optional… it would just be nice
to have it there if needed. In the end I honestly can see a more
natural process to testing methods bit by bit, building up examples and
stubs in their respective describe groups as needed. If someone
wants to single describe group and all stubs at the top, so be it. I’d
like mine to be incrementally built upon each other.

I’ll be quiet now and let others discuss, (…I’m cheering for this
though!)

On Jun 29, 2008, at 11:18 AM, Britt Mileshosky wrote:

requirements about finding an account and a person… I’ll write

  • shouldn’t find account
  • should …
    My instinct about this is that it would encourage long methods because
    it would make it less painful to test them, so I would be adverse to
    anything that let’s you short circuit the method.

Anybody else have opinions on that?

On Jun 29, 2008, at 11:38 AM, Britt Mileshosky wrote:

execution, then you only need to test your expectation

I understand your point, but should a testing framework be worried
too much about how a user will be writing their application code?

YES. This is Ruby, my friend, land of opinionated software. The whole
point of RSpec is to encourage good practices. Long Methods (any
method does more than one thing) are a known Code Smell. If you want
to write them, it should be painful.

You’re certainly entitled to a different opinion, and you can express
that opinion in a gem that extends rspec to do what you want! If you
do that and enough people use it and it proves generally useful and
pain-free, I’d be glad to peek at it again.

Cheers,
David

On Mon, Jun 30, 2008 at 2:59 AM, David C. [email protected]
wrote:

On Jun 29, 2008, at 11:38 AM, Britt Mileshosky wrote:

My instinct about this is that it would encourage long methods because
it would make it less painful to test them, so I would be adverse to
anything that let’s you short circuit the method.

Anybody else have opinions on that?

I agree with you here David. The long method syndrome is so easy to
fall into in ruby and rails especially. Many times I have shown some
of my team mates that coding small methods makes specing a lot easier
and containable.

Another way to go about this testing is to treat the controller method
as a public interface and only spec the ‘end result’ behaviour with a
given set of inputs using real objects (which has limits, but works in
most cases).

Britt, have a look at
http://lindsaar.net/2008/6/30/examples-of-behaviour-spec-n to see
what I mean.

Doesn’t totally solve the problem you are hitting now, but provides
you with another way to go about it (albeit a bit slower in spec
execution time)

On Sun, Jun 29, 2008 at 12:20 PM, David C. [email protected]
wrote:

  • in my example for making sure I find a user, I’d like to end execution

person does not belong to

  • should not find person
  • should …

My instinct about this is that it would encourage long methods because it
would make it less painful to test them, so I would be adverse to anything
that let’s you short circuit the method.

Anybody else have opinions on that?

I’m just catching up on email now after being sick for the past six
days,
but health aside my opinion is that I agree with David’s opinion. Rather
than focusing on how-to write easier tests that complain less, start
focusing on how-to write the right tests that complain when necessary.

One of the benefits associated with feeling the pain of a test is that
it
may be a sign to re-assess and refactor your code. This usually happens
early enough that it only takes a few minutes. Short circuiting
essentially
gives you the ability to not feel the pain. Its like CIPA [0], but for
code.
I would fear that the code would get so bad that by the time the test
cried
with pain your code was already beyond easy repair and instead required
invasive surgery.

Tests are part of the nervous system of your application. When they
hurt,
they’re telling you something isn’t right and that it should be
addressed,

0 -
http://en.wikipedia.org/wiki/Congenital_insensitivity_to_pain_with_anhidrosis

On Sun, Jun 29, 2008 at 9:18 AM, Britt Mileshosky
[email protected] wrote:

PeopleController with a logged in user

  • should find person
  • should assign person for the view

PeopleController with a logged in user who has an account the requested person does not belong to

  • should not find person
  • should …

Hi Britt,

Quick little thought experiment for you:

Imagine you write the examples as you have described, stubbing only
the minimum amount of things necessary and then ending the example
once you’ve reached a certain point. So for the action

def some_action
@user = self.current_user
@account = self.current_account if self.has_account?
@person = @account.people.find(params[:person])
end

your first example only runs until current_user, your second example
runs only until has_account, and your third one runs only until
account.people.find. So far so good.

What happens when you change the implementation to:

def some_action
@user = self.current_user
if self.has_account?
@account = self.current_account
@person = @account.people.find(params[:person])
end
end

which I could totally see doing, given that the original
implementation will raise an error on the @account.people call on
those occasions where has_account? returns false, making @account nil.

Can you spot the problem? Your specs will now fail because you had
only stubbed enough to get to a particular line. The specs are very
tightly coupled to the implementation, which sucks.

Also, if you were to do this incremental stubbing, you would have a
hard time moving examples around, because the nested examples would
rely on context set up by the parents.

This technique, after looking a bit more deeply at it, would lead to
specs that are tightly coupled to the implementation and to each
other. Tight coupling is usually bad, and tight coupling from both
angles is a recipe for total frustration.

I hope that is helpful.

Pat

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs