How can this pass?


#1

how could this test pass?

I have:

class PropertiesController < ApplicationController
def show
@property = Property.non_existing_method #causes a method missing
error
end
end

describe PropertiesController do
def mock_property(stubs={})
@mock_property ||= mock_model(Property, stubs)
end

describe “responding to GET show” do
it “should expose the requested property as @property” do
Property.should_receive(:non_existing_method).and_return(mock_property)
get :show, :id => “37”
assigns[:property].should equal(mock_property)
end
end
end

SB-MacBook-Pro:test sb$ ruby
./spec/controllers/properties_controller_spec.rb
.

Finished in 0.134667 seconds

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property? and why would the assigns[:property] be equal to
mock_property? did I eat some bad mushrooms?

Sergio


#2

Sergio B. wrote:

how could this test pass?

I have:

class PropertiesController < ApplicationController

why zero failures? more specifically, why would :non_existing_method
return mock_property? and why would the assigns[:property] be equal to
mock_property? did I eat some bad mushrooms?

Sergio

Ah… Maybe because of this?

Property.should_receive(:non_existing_method).and_return(mock_property)


#3

On Sun, Feb 8, 2009 at 5:35 PM, Sergio B. removed_email_address@domain.invalid
wrote:

   get :show, :id => "37"

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property? and why would the assigns[:property] be equal to
mock_property? did I eat some bad mushrooms?

That’s just the way this mock library works. It just makes sure it
receives
a particular message and then returns a certain value in response. It
doesn’t actually check to see whether the mocked object responds to that
message. Given Ruby’s dynamic nature, that check might not even be valid
at
the time.

Of course, this can be a source of false positives. That’s what you get
when
you test behavior instead of state.

///ark


#4

On Mon, Feb 9, 2009 at 2:35 AM, Sergio B. removed_email_address@domain.invalid
wrote:

   assigns[:property].should equal(mock_property)

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property?

Because you have stubbed the class method Property#non_existing_method
with:
Property.should_receive(:non_existing_method).and_return(mock_property)

It doesn’t make any difference whether this method is already defined
or not. Once the stub is set up, calls to the stubbed method will
return what you told it to return.

and why would the assigns[:property] be equal to
mock_property?

Because your controller’s #show method assigns @property with the
value returned from Property#non_existing_method (which you stubbed).

did I eat some bad mushrooms?

That I don’t know. Why do you think this behaviour is
strange/hallucinating?

Aslak


#5

On Sun, Feb 8, 2009 at 7:35 PM, Sergio B. removed_email_address@domain.invalid
wrote:

describe PropertiesController do
def mock_property(stubs={})
@mock_property ||= mock_model(Property, stubs)
end

describe “responding to GET show” do
it “should expose the requested property as @property” do
Property.should_receive(:non_existing_method).and_return(mock_property)

  • should_receive does not care if the object actually responds to the
    method - if you tell the object to expect it, it will respond to it

  • and_return is a declaration of a stub value to return when the
    object receives the message that it should receive

Put that together, and this line says exactly what it looks like it
says:

The Property class should receive :non_existing_method, and when it
does, it should return mock_property to whatever code is calling it.

That make any sense now?


#6

Aslak Hellesøy wrote:

On Mon, Feb 9, 2009 at 2:35 AM, Sergio B. removed_email_address@domain.invalid
wrote:

   assigns[:property].should equal(mock_property)

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property?

Because you have stubbed the class method Property#non_existing_method
with:
Property.should_receive(:non_existing_method).and_return(mock_property)

It doesn’t make any difference whether this method is already defined
or not. Once the stub is set up, calls to the stubbed method will
return what you told it to return.

and why would the assigns[:property] be equal to
mock_property?

Because your controller’s #show method assigns @property with the
value returned from Property#non_existing_method (which you stubbed).

did I eat some bad mushrooms?

That I don’t know. Why do you think this behaviour is
strange/hallucinating?

Aslak

ah! okay it’s starting to make sense… and the mushroom effect is
wearing out. just kidding :wink:

Thank you guys. By the way I just bought the “Rspec Book” and it’s seems
like a great resource. It has encouraged me to learn the framework.

Sergio


#7

2009/2/8 Sergio B. removed_email_address@domain.invalid

   get :show, :id => "37"

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property? and why would the assigns[:property] be equal to
mock_property?

Because that’s what you’re saying with your and_return call above. The
and_return method is not part of the expectation: it allows you to
specify
what value you want to be returned each time the expected message is
received.

You can find more info at
http://rspec.info/documentation/mocks/message_expectations.html.

Regards,
Matías A. Flores


#8

On Sun, Feb 8, 2009 at 8:54 PM, Sergio B. removed_email_address@domain.invalid
wrote:

ah! okay it’s starting to make sense… and the mushroom effect is
wearing out. just kidding :wink:

Thank you guys. By the way I just bought the “Rspec Book” and it’s seems
like a great resource. It has encouraged me to learn the framework.

That’s great news, thanks! If you have any constructive feedback to
provide about the book, please do so at:

http://www.pragprog.com/titles/achbd/errata
http://forums.pragprog.com/forums/95

Cheers,
David


#9

On Sun, Feb 8, 2009 at 6:09 PM, Matías Flores removed_email_address@domain.invalid
wrote:

You can find more info at http://rspec.info/documentation/mocks/message_expectations.html.

On that page, I found this example of using a computed return value
with an expectation:

my_mock.should_receive(:msg).with(:numeric, :numeric) once.and_return
{|a, b| a + b}

I’m not familiar with :numeric. Does it mean what it looks like it
means?

///ark


#10

On Sun, Feb 8, 2009 at 6:54 PM, Sergio B. removed_email_address@domain.invalid
wrote:

On Mon, Feb 9, 2009 at 2:35 AM, Sergio B. removed_email_address@domain.invalid
wrote:

   assigns[:property].should equal(mock_property)

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property?

Dogpile!

///ark


#11

2009/2/9 Mark W. removed_email_address@domain.invalid

my_mock.should_receive(:msg).with(:numeric, :numeric) once.and_return
{|a, b| a + b}

I’m not familiar with :numeric. Does it mean what it looks like it means?

That example is a bit outdated. Use of symbols as mock argument
constraints
such as :numeric, :string, :anything, etc, are deprecated.

The expectation above should be replaced with:

my_mock.should_receive(:msg).with(an_instance_of(Numeric),
an_instance_of(Numeric)).once.and_return {|a, b| a + b}

Regards,
Matías


#12

On Mon, Feb 9, 2009 at 6:22 AM, Matías Flores removed_email_address@domain.invalid
wrote:

On that page, I found this example of using a computed return value
with an expectation:

my_mock.should_receive(:msg).with(:numeric, :numeric) once.and_return
{|a, b| a + b}

I’m not familiar with :numeric. Does it mean what it looks like it means?

That example is a bit outdated.

And now fixed:
http://rspec.info/documentation/mocks/message_expectations.html


#13

Mark W. wrote:

On Sun, Feb 8, 2009 at 6:54 PM, Sergio B. removed_email_address@domain.invalid
wrote:

On Mon, Feb 9, 2009 at 2:35 AM, Sergio B. removed_email_address@domain.invalid
wrote:

   assigns[:property].should equal(mock_property)

1 example, 0 failures

why zero failures? more specifically, why would :non_existing_method
return mock_property?

Dogpile!

///ark

Evidently I’m still not fully getting it… why would this not pass? I
mean I know why it doesn’t pass. One instance is a mock object and the
other is an AR object. But, shouldn’t it return the mock property?
There’s just something elemental about Rspec I still don’t get.

describe “responding to GET new” do

it "should expose a new property as @property" do
  @account = mock_model(Account)
  @account.should_receive(:properties)
  @property = mock_model(Property, :new_record? => false, :errors => 

[])
@account.properties.should_receive(:build).and_return(@property)
get :new
assigns[:property].should == @property
end

end

def new
@property = @account.properties.build

respond_to do |format|
  format.html # new.html.erb
  format.xml  { render :xml => @property }
end

end

‘PropertiesController responding to GET new should expose a new property
as @property’ FAILED
expected: #<Property:0x1191b84 @name=“Property_1002”>,
got: #<Property id: nil, name: nil, address: nil, city: nil, state:
nil, zip: nil, uasap: nil, tax_number: nil, rent_due: nil, units_count:
0, issues_count: 0, account_id: 1, created_at: nil, updated_at: nil>
(using ==)


#14

On Mon, Feb 9, 2009 at 10:18 PM, Sergio B. removed_email_address@domain.invalid
wrote:

other is an AR object. But, shouldn’t it return the mock property?
[])
respond_to do |format|
got: #<Property id: nil, name: nil, address: nil, city: nil, state:
nil, zip: nil, uasap: nil, tax_number: nil, rent_due: nil, units_count:
0, issues_count: 0, account_id: 1, created_at: nil, updated_at: nil>
(using ==)

Posted via http://www.ruby-forum.com/.


rspec-users mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users

Hi in your new method, where does @account come from? Is it loaded
through some kind of before_filter, or as part of the authentication
framework?

Anyway, once you’ve built up your mock expectations you now need to
inject the mock into the code. You do this by stubbing the code that
loads up the @account object - typically you’re just doing an
Account.find call somewhere. Note also that you need to specify that
account returns something when properties is called - you need to hook
up the association proxy.

it “should expose a new property as @property” do
@account = mock_model(Account)
Account.should_receive(:find).with(“1”).and_return @account
@account.stub!(:properties).and_return mock(‘properties proxy’)
@property = mock_model(Property, :new_record? => false, :errors =>
[])
@account.properties.should_receive(:build).and_return(@property)
get :new
assigns[:property].should == @property
end
end

additionally I would only have two expectations (should_receive) and
would loosen the rest by just stubbing:

it “should expose a new property as @property” do
# arrange
@account = mock_model(Account, :properties => mock(‘properties
proxy’))
@property = mock_model(Property, :new_record? => false, :errors =>
[])
@account.properties.stub!(:build).and_return(@property)

 # sanity check
 Account.should_receive(:find).with("1").and_return @account

 # act
 get :new

 # assert
 assigns[:property].should == @property

end
end

I like to minimize the number of expectations(/assertions) that appear
in the example. Doing so clearly communicates the important behavior.
If the final expectation passes, you can infer that it’s doing the
right thing with the model because that’s the only way to get to your
mock @property. Finally I threw in an Account.should_receive(“1”)
because you need to inject the mock account somehow. That does the
trick, and it also serves as a sanity check for the contract between
controller and model - Account.find is the entry point to the model in
this action.

Does that make sense?

Pat


#15

On Mon, Feb 9, 2009 at 10:18 PM, Sergio B. removed_email_address@domain.invalid
wrote:

end

end

Another thing is that instance variables shouldn’t be used unless
necessary. As a rule of thumb, things should have as limited a scope
as possible. Prefer class variables to global variables, instance
variables to class variables, and local variables to instance
variables.

Some reasons are 1) to better communicate your intent, 2) to limit the
places you have to look to see where a variable is being changed, and
3) to limit the amount of code that might depend on the variable.

///ark


#16

Sergio,

Responding to your original question. This issue has bugged me a lot
in the past 1 year. And apparently, a lot of other people. As already
explained, the spec should indeed pass. But there should be a failure
somewhere because your code will obviously not work.

Synthesis is a great tool for checking this stuff automatically. Read
a bit about it here (http://nutrun.com/weblog/synthesized-testing-a-
primer/) and here (http://github.com/gmalamid/synthesis/tree/master)

Pradeep


#17

Pat M. wrote:

On Mon, Feb 9, 2009 at 10:18 PM, Sergio B. removed_email_address@domain.invalid
wrote:

other is an AR object. But, shouldn’t it return the mock property?
[])
respond_to do |format|
got: #<Property id: nil, name: nil, address: nil, city: nil, state:
nil, zip: nil, uasap: nil, tax_number: nil, rent_due: nil, units_count:
0, issues_count: 0, account_id: 1, created_at: nil, updated_at: nil>
(using ==)

Posted via http://www.ruby-forum.com/.


rspec-users mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users

Hi in your new method, where does @account come from? Is it loaded
through some kind of before_filter, or as part of the authentication
framework?

Anyway, once you’ve built up your mock expectations you now need to
inject the mock into the code. You do this by stubbing the code that
loads up the @account object - typically you’re just doing an
Account.find call somewhere. Note also that you need to specify that
account returns something when properties is called - you need to hook
up the association proxy.

it “should expose a new property as @property” do
@account = mock_model(Account)
Account.should_receive(:find).with(“1”).and_return @account
@account.stub!(:properties).and_return mock(‘properties proxy’)
@property = mock_model(Property, :new_record? => false, :errors =>
[])
@account.properties.should_receive(:build).and_return(@property)
get :new
assigns[:property].should == @property
end
end

additionally I would only have two expectations (should_receive) and
would loosen the rest by just stubbing:

it “should expose a new property as @property” do
# arrange
@account = mock_model(Account, :properties => mock(‘properties
proxy’))
@property = mock_model(Property, :new_record? => false, :errors =>
[])
@account.properties.stub!(:build).and_return(@property)

 # sanity check
 Account.should_receive(:find).with("1").and_return @account

 # act
 get :new

 # assert
 assigns[:property].should == @property

end
end

I like to minimize the number of expectations(/assertions) that appear
in the example. Doing so clearly communicates the important behavior.
If the final expectation passes, you can infer that it’s doing the
right thing with the model because that’s the only way to get to your
mock @property. Finally I threw in an Account.should_receive(“1”)
because you need to inject the mock account somehow. That does the
trick, and it also serves as a sanity check for the contract between
controller and model - Account.find is the entry point to the model in
this action.

Does that make sense?

Pat

Thanks Pat. You’re right. Account comes from a before_filter that
triggers this:

def current_account
@account ||= Account.find_by_subdomain(account_subdomain)
end

I have stubbed the code on the spec_helper.rb to do that as:

def current_account(account)
@controller.stub!(:current_account).and_return(@account = account ?
accounts(account) : nil) #should return account fixture
end

on the property spec now I have:

describe PropertiesController do
fixtures :accounts, :properties, :users

before(:each) do
current_account(:default)
login_as(:default)
end

describe “responding to GET new” do

it "should expose a new property as @property" do
  property = mock_model(Property, :new_record? => false, :errors => 

[])
@account.properties.should_receive(:build).and_return(property)
get :new
assigns[:property].should == property
end

end
end

but now the controller complains that @account is nil:

The error occurred while evaluating nil.properties
/www/rentcloud2/app/controllers/properties_controller.rb:23:in `new’

I thought by stubbing the current_account code and returning @account,
that’d be available to the application.