[newbie]: How to test for operations inside a block?

Hi to everyone,

I’m just trying to wrap my mind around BDD and RSpec. I’ve gone through
“The rails’ way” and RSpec webcasts series, and still don’t have a clear
vision of RSpec. Is there anything else I could read/watch?

Back in topic…
Now I’m writing specs for a “create” action.
The action is written like this:

def create
authenticate_with_open_id(params[:identity_url], :required => [
:nickname, :email ], :optional => :fullname) do |result, identity_url,
registration|
if result.successful?
@user = User.new
@user.identity_url = identity_url
@user.roles << Role.find_by_name(‘operator’)
@user.user_type = ‘operator’
assign_registration_attributes!(registration)
@user.save(false)
redirect_to users_path
else
redirect_to new_user_path
end
end
end

How am I supposed to write a spec to test the code shown above?

I tried with:

it “should check identity_url via OpenID authentication” do
result = mock(‘Object’)
result.should_receive(:successful?).and_return(:true)
@controller.should_receive(:authenticate_with_open_id).with(@identity_url,
:required => [ :nickname, :email ], :optional =>
:fullname).and_return(result)
do_verb
result.should be_successful
end

it “should create a new user using OpenID authentication” do
@controller.stub!(:authenticate_with_open_id)
@user = new_user # thanks to fixture_replacement2
User.stub!(:new).and_return(@user)
@user.should_receive(:save).with(false).and_return(true)
do_verb
end

The first test pass successfully while the second throws havocs on me.
It complains on the “@user.should_receive(:save…” part.
It also complained when I wrote
“User.should_receive(:new).and_return(@user)”, so I changed
it to “User.stub!(:new)…”. But I don’t fully understand why I had to
do so.

Thanks in advance for your help,
Carmine

On Mon, Sep 22, 2008 at 9:07 AM, Carmine M.
[email protected]wrote:

   assign_registration_attributes!(registration)
   @user.save(false)
   redirect_to users_path
 else
   redirect_to new_user_path
 end

end
end

How am I supposed to write a spec to test the code shown above?

The first thing I would do would be to refactor this code into the User
model. The controller shouldn’t be concerned with assigning the
user_type,
for example.

The first test pass successfully while the second throws havocs on me.

It complains on the “@user.should_receive(:save…” part.
It also complained when I wrote
“User.should_receive(:new).and_return(@user)”, so I changed
it to “User.stub!(:new)…”. But I don’t fully understand why I had to
do so.

Others may have more insight, but I would want to know what the specific
complaints were.

///ark

Hi Mark,

First off, thanks a lot for your reply!

Mark W. wrote:

On Mon, Sep 22, 2008 at 9:07 AM, Carmine M.
[email protected]wrote:

   assign_registration_attributes!(registration)
   @user.save(false)
   redirect_to users_path
 else
   redirect_to new_user_path
 end

end
end

The first thing I would do would be to refactor this code into the User
model. The controller shouldn’t be concerned with assigning the
user_type,
for example.

Care to explain a little more?
I left the code in the controller because it’s the controller that knows
about
the context in which the gets created.

Should I have written one or more class methods on User to accomplish
the task
of setting properties properly?

“user_type” is something I forgot to remove, and it has been replaced by
roles.

Others may have more insight, but I would want to know what the specific
complaints were.

The error shown is:

Mock ‘User’ expected :save with (false) once, but received it 0 times

[…huge cut…]

In general, the mantra is “skinny controllers, fat models,” meaning that

Indeed, while waitin for a reply, I googled a bit and came right to the
“skinny controllers, fat models” post!

So I’ve refactored the user creation code into a class method on User.

User.create_with_role(:role, identity_url, registration)

Thus cleaning a bit the whole code.
Thanks!

It looks like you want to use something like
@controller.should_receive(:authenticate_with_open_id).and_yield()

I didn’t know about and_yield() before. Cool!

I will surely have a look at the “and_yield()”, thanks!

Again, thanks for your precious help and time!

On Mon, Sep 22, 2008 at 9:07 AM, Carmine M.
[email protected]wrote:

   assign_registration_attributes!(registration)
   @user.save(false)
   redirect_to users_path
 else
   redirect_to new_user_path
 end

end
end

In general, the mantra is “skinny controllers, fat models,” meaning that
if
you can put code in the model, you probably should. For no other reason,
because it makes testing easier and faster. Controllers should (again,
in
general) act as dispatchers to the code that really does the work: the
models to handle the logic, and the views to handle the presentation.
Searching on the mantra above will probably explain this more fully.

In this case, it appears to me that you’re creating a new user based on
some
OpenID information. There’s nothing in that code that’s specific to
POSTing
certain information to a certain URL in your app, which is what
controllers
handle. How a User gets created from OpenID information is really the
User
class’s responsibility, in my opinion. It’s business logic, and you like
to
keep business logic as close as possible to its object. While we don’t
generally like to program for the future, it’s easy to see that if
business
logic is spread over controllers as well as models, it’s going to be
harder
to debug, maintain, etc.

it “should create a new user using OpenID authentication” do

“User.should_receive(:new).and_return(@user)”, so I changed
it to “User.stub!(:new)…”. But I don’t fully understand why I had to
do so.

It looks like you want to use something like
@controller.should_receive(:authenticate_with_open_id).and_yield()

I didn’t know about and_yield() before. Cool!

///ark

Carmine, someone could just give you the answer, but pardon me if I get
a
little Socratic on your ass*

Given this code (which is similar to the code you’re testing):

authenticate() do |result|
puts result
end

do you understand what’s going on? When is ‘puts result’ executed? What
code
causes it to be executed? Where does ‘result’ get its value? What does
the
‘yield’ keyword do? If you wanted to replace authenticate() with a stub
for
testing, how would ‘result’ get its value? Do you understand the purpose
of
and_return in RSpec mocking? What would and_yield then do?

If you’re unclear on any of these points, take a step back and refresh
your
memory on the subject of blocks and methods that take blocks (like
authenticate_with_open_id()) . You’ll be glad you did!

///ark

*Pulp Fiction reference, in case someone reading this thinks I’m merely
being obnoxious.

So, after spending a little time trying to work with “and_yield”, I
strongly believe that:

  • Either I haven’t understood what “and_yield” does

    • And this is more than possible
  • Or “and_yield” is not the toy I should be playing with.

What I’m trying to accomplish is to writing tests for the following code
(refactored after great advices from ///ark):

def create
authenticate_with_open_id(params[:identity_url], :required => [
:nickname, :email ], :optional => :fullname) do |result, identity_url,
registration|
if result.successful?
@user = User.create_with_role(:operator, identity_url,
registration)
redirect_to users_path
else
redirect_to new_user_path
end
end
end

I believe I should test that “authenticate_with_open_id” gets called on
@controller, and that
“create_with_role” gets called on “User” class.

So I wrote this:

def do_verb(options = { :identity_url => @identity_url })
post :create, options
end

it “should create a new user using OpenID authentication” do
@user = create_operator_user
@controller.should_receive(:authenticate_with_open_id)
User.should_receive(:create_with_role).and_return(@user)
do_verb
response.should redirect_to(users_path)
end

The test works up to the point where I set expectations on
“:authenticate_with_open_id”, then
it fails with:
Mock ‘Class’ expected :create_with_role with (any args) once, but
received it 0 times

I tried this:

 @controller.should_receive(:authenticate_with_open_id).and_yield do 

|a,b,c|
User.should_receive(:create_with_role).and_return(@user)
end

and this:

 @controller.should_receive(:authenticate_with_open_id).and_yield
  User.should_receive(:create_with_role).and_return(@user)

Both leading to the following error:

Mock ‘UsersController’ yielded || to block with arity of 3

Where am I doing wrong?

Thanks in advance for your help,
Carmine that really can’t wrap his mind around RSpec :confused:

I came up with this code:

def mock_result(stubs = {})
@result ||= mock(‘Object’, stubs)
end

describe "being an administrator" do
  it "should create a new user using OpenID authentication" do

@controller.should_receive(:authenticate_with_open_id).with(@identity_url,
anything()).and_yield(mock_result(:successful? => true),
@identity_url, mock_registration)
User.should_receive(:create_with_role).and_return(@user =
create_operator_user)
do_verb
response.should redirect_to(users_path)
end
end

which doesn’t give any error and seems pretty fine to me. Is this the
way it is meant to be done?

Thanks for your help

On Tue, Sep 23, 2008 at 3:39 AM, CarmineM [email protected]
wrote:

anything()).and_yield(mock_result(:successful? => true),

Without knowing everything about your code, that looks almost there!
You’re
basically specing that when the create action is called (from do_verb):

  1. authenticate_with_open_id should get called with certain parameters
  2. When it does get called, have it yield certain other parameters to
    its
    block
  3. User.create_with_role gets called and returns something returned by
    create_operator_user

I would ask:

  1. What arguments should User.create_with_role be called with? These
    should
    be specified (tested).
  2. What is the return value of User.create_with_role, and what should be
    done with it? Answer: it returns a user, which should end up getting
    assigned to the controller’s @user. This specification is accomplished
    with
    assigns[:user].should == <the mock user object you’re returning from
    User.create_with_role>.

As far as “the way” to use RSpec in situations like this, I use TDD/BDD.
Basically, this means that we don’t write any code without a failing
test.
Think about that. It’s a pretty weird concept. But it works.

One way to achieve this ex post facto would be to comment out all your
controller code. Write the simplest spec that describes something you
want
to have happen. It will fail. Then comment back in the code (and just
the
code) to make the test pass. After it passes, see if the commented in
code
expresses its intent well (in other words, clean it up if necessary).
Then
think of another test (or “description of behavior”) to write. It
fails,
you make it pass, you clean it up. And so on. Continue this cycle until
you
can’t think of any more tests - in other words, everything you want the
code
to do has been described/verified/tested.

This has several benefits: 1) You take tiny steps, which are so much
easier
than any other kind of steps. 2) You make sure you haven’t missed any
specifications, because every line of code is described before it’s even
been written, and 3) You move away from thinking “is the code I just
wrote
correct?” to “what should this code I’m about to write actually
accomplish”?

Also, try to match RSpec’s grammar with regard to the “describe” and
“it”
blocks, such that they stand alone as documentation (though I can never
remember the RSpec command to produce it). In the above spec, you’re
saying
“being an administrator, it should create a new user using OpenID
authentication.” In reality, what you want to describe is what happens
when
someone POSTs data to your create action. That may have further context
applied, such as “when the poster is an administrator”.

What line of code does all this describe? The do_verb call. So if I were
writing this from scratch, that’s the first line of code I’d write in
the
spec (presumably, something like ‘post :create, :identity_url =>
‘asdf’’).
I’d run this line of code and it would fail (I think) because that
action
doesn’t exist in the controller (because we commented it out). So the
next
step would be to do the simplest thing that could make the test pass;
obviously, defining the controller method (without anything in it).

What should the next test be? Well, you could test that
authenticate_with_open_id is called. So you’d set up an expectation on
that
method: @controller.should_receive(authenticate_with_open_id). You’d run
the
test, it would fail, and then you’d add the call. Now, what parameters
should authenticate_with_open_id receive? Specify them. Test fails. Add
them. Test passes.

If you go through this process with every single line of code you write,
not
only do the tests become obvious as you go along, but so does the code
under
test. You might find that (as in your original code), you have to set up
a
lot of tests on what happens to the new user. As a result, you might
realize
that the controller shouldn’t care about these details, and that you
should
write User.create_with_role (for which you’ll eventually write its own
tests). This is an example of how when testing is tedious or difficult,
it
may point to the code not being structured correctly.

Good lord; listen to me rattle on. :slight_smile: I’m pretty confident in everything
I’ve said here, but it’s been explained a lot better by others. Perhaps
the
best way to get more information on how to use RSpec is to google TDD
and/or
BDD. And stay on this list, paying especially attention to posts by
people
like David and Pat (as well as others).

Good luck!

///ark

Mark, if by any chance you should ever be in Italy (near by Naples),
please drop me a line
I owe you a lot more than a beer, but that would do for a start :).

Thanks so much for every word and every second spent in helping me!

I had to write tests after some code has been written, because I’m
struggling
with my bosses to let them understand how can BDD improve the way we
work.

Thanks again!

On Tue, Sep 23, 2008 at 10:27 AM, CarmineM [email protected]
wrote:

Mark, if by any chance you should ever be in Italy (near by Naples),
please drop me a line

Ooo, that’s a deal! My son’s in his third year of Italian, and I just
made
my first ragu Bolognese. :slight_smile:

///ark

Mark W. wrote:

Carmine, someone could just give you the answer, but pardon me if I get
a
little Socratic on your ass*

I do appreciate the fact you’re not going to give me the answer, but the
pointers.

And, at the risk of appearing dumber than what I am, I’ll try to reply.

Given this code (which is similar to the code you’re testing):

authenticate() do |result|
puts result
end

do you understand what’s going on? When is ‘puts result’ executed? What
code
causes it to be executed? Where does ‘result’ get its value? What does
the
‘yield’ keyword do? If you wanted to replace authenticate() with a stub
for

When authenticate gets called, it will call the associated block passing
“result” to it,
so that the code within the block can do its operations over “result”
(and perhaps return
something to the caller).

testing, how would ‘result’ get its value? Do you understand the purpose
of
and_return in RSpec mocking? What would and_yield then do?

If you’re unclear on any of these points, take a step back and refresh
your
memory on the subject of blocks and methods that take blocks (like
authenticate_with_open_id()) . You’ll be glad you did!

I did re-read the topic of blocks in ruby (Programming Ruby 2nd Ed.,
page 47-49) which,
taking into account my bad memory, can’t do any harm.

What I don’t have very clear is the whole RSpec way of working and, yes,
I’ve
watched the Peepcode’s RSpec series many times and still have this
feeling.

Now, the “and_return” is about the “return value of the mock”, while the
“and_yield” is
about “passing something to the associated block”. Right?

Having said that, Carmine.expects(:something).like_that(:to_work):

 @user = create_operator_user
 result = mock('Object')

result.should_receive(:successful?).and_return(:true)

 registration = %w( a b c )
 @controller.should_receive(:authenticate_with_open_id).and_yield(result,@identity_url,registration) 

do |a_result,an_identity,a_registration|
puts “Hello world”
—>>A a_result.should_receive(:successful?).and_return(:true)
—>>B User.should_receive(:create_with_role).and_return(@user)
response.should redirect_to(users_path)
end
do_verb

Which doesn’t work at all.
The complaints are about the row marked with:
—>>A: Mock ‘Object’ received unexpected message :successful? with (no
args)

if I remove the # on “result.should…” this error disappears, but I get
the one at “—>>B”
which I guess is for the very same reason of the first one.

It seems to me that the code in the block never gets called, indeed I
prooved it by placing
a “puts” statement in it whose message doesn’t get printed.

Now, It would be nice if you could give me pointers to RSpec
docs/examples suitable for dumbs
like me.

*Pulp Fiction reference, in case someone reading this thinks I’m merely
being obnoxious.

Great movie.

I really thank you for your patience and time.