views:

687

answers:

5

I'm trying to use rspec's mocking to setup expectations that I can verify in the it "should" methods... but I don't know how to do this... when i call the .should_receive methods on the mock, it verifies the expected call as soon as the before :all method exits.

here's a small example:

describe Foo, "when doing something" do
 before :all do
  Bar.should_recieve(:baz)
  foo = Foo.new
  foo.create_a_Bar_and_call_baz
 end

 it "should call the bar method" do
  # ??? what do i do here?
 end
end

How can i verify the expected call in the 'it "should"' method? do i need to use mocha or another mocking framework instead of rspec's? or ???

+1  A: 

As a general rule you should never put an expectation in a before block. before blocks are for setting up state that is shared across several examples (it blocks). Put the expectation in the example itself. E.g.:

describe Foo, "when doing something" do
  before :all do
   @foo = Foo.new
  end

  it "should call the bar method" do
    Bar.should_recieve(:baz)
    @foo.create_a_Bar_and_call_baz
  end
end

I generally try to have my describe block describe a particular state (e.g. describe Car, "given a full tank of gas"), rather than describing an action.

Avdi
that doesn't work when i have more than one expectation to verify...<pre><code>describe Foo, "when..." do it "should do this" it "should do that" it "should do another thing"end</code></pre>I suppose I could do a before :each in that case, but that would signficiantly increase the time to execute my functional/integration tests, due to resetting state of external resources, etc.can rspec's mock not handle what I'm wanting? if not, can mocha?
Derick Bailey
Derick, if I understand your problem correctly, I believe this may be a scenario in which I have used `after :each` to good effect. So I would have an `after :each` block which called the method under test, and I'd set up a different expectation in each `it` block.
Avdi
Also, judging by a previous question of yours I'm curious if you are familiar with stubs vs. mocks? Stubs are like `should_receive` only they don't set an expectation, they just stub out some behavior. There's nothing wrong with setting up stubs in a `before :each` block. See here for more: http://rspec.info/documentation/mocks/stubs.html
Avdi
Thanks, Advi. the use of after :each seems sufficient for what I'm wanting to do.re: mock vs. stub - yeah, i understand the difference. been doing mocks/stubs in c# for 3+ years now. i can see how my questions might make you ask that, though. :)
Derick Bailey
Advi - can you post the 'after :all' info as a direct answer, so I can mark it as the answer? I want to give you credit, but want to make sure it's for the info that i'm using.
Derick Bailey
A: 

Maybe I'm looking at it simplistically, but, do you need to verify that interaction with Bar more than once? If so, ok, but I'm not sure. In other words, are you mixing contexts?

If not, then it's not really context so much as it's part of the observation, which would make it naturally belong to the it block.

Scott C. Reynolds
I understand what you're saying, and I agree with it. the example that I've shown here is overly simplistic. the real situation I'm dealing with has a class that I'm testing and a class that I need to mock. the class that I need to mock is called twice during the execution of one method in the class I'm testing - once to "start" and once to "exec!"... it's an SSH connection / command execution.
Derick Bailey
the problem that I have with putting the expectation in the 'it' block is that it would require me to call the "foo.create_a_Bar_and_call_baz" method (from the above example) in every "it" block, which is ugly and difficult to maintain.I'm coming at this from a C#/.NET BDD experience with Rhino Mocks, where I always set the mock's expectations in the 'it' blocks, but i only execute the class under test in the setup, once.i expect to be able to do similar things with mock objects in ruby. it makes sense, and it should be supported. ... or am i off my rocker again?
Derick Bailey
A: 

The should_receive method is used to setup your mock object to return something specific when the method is called. Here's an example:

Bar.should_recieve(:baz).with.({:arg1 => 'this is arg1', :arg2 => 'this is arg2'}).and_return(true)

Typically, with BDD, you want to test the behavior of your app. Testing which methods were called in order to build the proper behavior is irrelevant. If one day you decide to remove the baz method, you'll have to update your tests even though the behavior of your app won't have changed.

I think you're trying to use should_receive in a way that it wasn't meant to work like.

Louis Salin
i agree that we want to test behavior. i disagree w/ your conclusion that method calls are not behaviour. sometimes the behavior of the class under test is only known by which methods are called on another object. if i did not test these calls, then I would only be testing getter/setter data (properties, attributes, whatever) which would not be testing behavior at all.it seems that rspec mocks do not support assertion of expectations outside of the expectation setup, which is a failure of rspec, not the way i'm testing. i'll probably switch to mocha
Derick Bailey
I've used both Mocha and RSpec's mocks (which are based on flexmock) extensively. As far as I know they are functionally equivalent; in some cases RSpec's may be more powerful. I think there is probably a better way to accomplish what you are trying to do.
Avdi
+1  A: 

I'm going to take another whack at this, because it's clear from the initial set of answers and responses that there was some confusion about what you are trying to accomplish. Let me know if this is closer to what you are trying to do.

describe Foo, "when frobbed" do
  before :all do
    @it = Foo.new

    # Making @bar a null object tells it to ignore methods we haven't 
    # explicitly stubbed or set expectations on
    @bar = stub("A Bar").as_null_object
    Bar.stub!(:new).and_return(@bar)
  end

  after :each do
    @it.frob!
  end

  it "should zap a Bar" do
    @bar.should_receive(:zap!)
  end

  it "should also frotz the Bar" do
    @bar.should_receive(:frotz!)
  end
end

Incidentally, although it works I'm not a big fan of the Bar.stub!(:new) pattern; I usually prefer to pass collaborators in via optional arguments, e.g. @it.frob!(@bar). When not given an explicit argument (e.g. in the production code), the collaborator can be defaulted: def frob!(bar=Bar.new). This keeps the tests a little less bound to internal implementation.

Avdi
thanks, Avdi. the after :each is working well. i agree w/ the dependency injection bit, too. that's how i handle it in C#... still learning what does / does not translate well in ruby.
Derick Bailey
A: 

I know this is an old conversation but just in case somebody still needed a right answer, here I write an example on how to validate rpec mocks spectations:

require 'spec'
require 'spec/mocks'
include Spec::Mocks::ExampleMethods

o = mock('object')
o.should_receive(:respond_to?).once

space = Spec::Mocks::Space.new
space.add o

# here we should invoke methods, o.respond_to?:foo for instance

space.verify_all

cheers

David Calavera