Forum: RSpec how can this pass?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Sergio B. (Guest)
on 2009-02-09 03:35
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
James B. (Guest)
on 2009-02-09 04:02
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)
Mark W. (Guest)
on 2009-02-09 04:22
(Received via mailing list)
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
Aslak H. (Guest)
on 2009-02-09 04:34
(Received via mailing list)
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
David C. (Guest)
on 2009-02-09 04:44
(Received via mailing list)
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?
Sergio B. (Guest)
on 2009-02-09 04:54
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 ;)

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
Matías Flores (Guest)
on 2009-02-09 06:11
(Received via mailing list)
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_expe....

Regards,
Matías A. Flores
David C. (Guest)
on 2009-02-09 06:28
(Received via mailing list)
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 ;)
>
> 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
Mark W. (Guest)
on 2009-02-09 07:50
(Received via mailing list)
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
Mark W. (Guest)
on 2009-02-09 09:09
(Received via mailing list)
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_expe....
>

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
Matías Flores (Guest)
on 2009-02-09 14:24
(Received via mailing list)
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
David C. (Guest)
on 2009-02-09 17:04
(Received via mailing list)
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_expe...
Sergio B. (Guest)
on 2009-02-10 08:18
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 ==)
Pat M. (Guest)
on 2009-02-10 11:42
(Received via mailing list)
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
Mark W. (Guest)
on 2009-02-10 17:21
(Received via mailing list)
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
Pradeep G. (Guest)
on 2009-02-10 21:53
(Received via mailing list)
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
Sergio B. (Guest)
on 2009-02-11 05:55
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.
This topic is locked and can not be replied to.