[rspec-users] a MockExpectationError problem

Phillip Koebbe
Sun, 25 Apr 2010 21:51:26 -0000

On 2010-04-25 4:25 PM, Patrick J. Collins wrote:
> On Sun, 25 Apr 2010, Phillip Koebbe wrote:
>> See http://gist.github.com/378489 for how I ended up specing a file
upload.
>> This isn''t the exact same situation, but maybe you will get
some ideas that
>> will help. And there may be a better way, but this seems to work and is
fairly
>> easy for me understand.
> Yeah, I see what you''re doing, but I don''t quite get how
I could apply this to
> what I am doing.
>

I was just thinking that since your problem had to do with files on the
file system getting overwritten, seeing how to someone worked around
that problem with uploads might trigger some ideas in your mind.

> I have a method that creates a contact and then creates a photo and assigns
it
> to that contact.
>
> So my problem comes from doing (in my model method):
>
> @contact.photo = Photo.create(...)
>
> ...  Although the code works fine in the real world app use, the spec fails
> because it''s trying to do something with "[]=" .. which
I don''t get.
>

I may be mistaken, but []= looks like an array assignment, which would
suggest to me something to do with adding an element to a collection
somewhere.

Do you have any hooks defined that affect the creation process? Or is
this a straight-forward, vanilla Photo.create call that goes straight
into ActiveRecord? Maybe you should gist your spec and file.

> ----
>
> Also, I have a couple side questions--
>
> 1) is there some sort of caching done to before(:all/:each) blocks?
>
> I had a before block that set an instance variable to something, and then I
> removed it-- and was getting weird behavior, and after doing some
inspecting I
> found that the instance variable was still set-- even though it was not set
> anywhere in the code...  I had to put a blank before block in there to get
that
> behavior to stop...  Just wondered if this is normal?
>

I''ve not experienced that problem, so I can''t help with this.

> 2)  I still am struggling with mock vs stub-- or I should say stub! vs.
> should_receive.  Initially I had something like this:
>
> before(:all) do
>    foo = mock_model)
>    Foo.should_receive(:find_by_name).with("bar").and_receive(foo)
> end
>
> it "blah" do
>    3.times {  @blah.do_something }
> do
>
> it "blah2" do
>    3.times { @blah.do_something }
> end
>
> (blah model)
> def do_something
>    @foo = Foo.find_by_name("bar")
> end
>
> This wasn''t working, because apparently it''s only good
for one usage.  I was
> thinking that declating "should_receive" would mean that anytime
Foo gets
> .find_by_name called on it, it will have that behavior.  I ended up
changing
> that to a before(:each), and then I got errors like expected it 1 time but
got
> it 3 times....
>
> So not knowing how to deal with that, I changed .should_receive to .stub!
and
> then the errors went away.
>
> Not sure if that was the right thing to do in that case.
>

Use stub when you just want to provide the plumbing. Use should_receive
when you are setting an expectation. As an example, you could do
Photo.stub(:create) just to provide the method to avoid errors when your
code calls it. But if you actually want to verify that your code calls
it, then do Photo.should_receive(:create). After David gave his input a
few weeks ago, I''ve adopted an approach in which I stub methods in
before(:each), including return values where appropriate, and then put
an expectation in an example. So I might have something like:

before(:each) do
     widget = stub_model(Widget).as_new_record
     Widget.stub(:new).and_return(widget)
end

it ''should call Widget.new'' do
     Widget.should_receive(:new)
     get :new
end

it ''should render the new template'' do
     get :new
     response.should render_template(:new)
end

In this example, the real code is going to call .new both times, but
I''m
setting an expectation just once (trying to follow the convention of one
test per example). In the first example, where the expectation is set,
I''ll get a failure if .new isn''t called. In the second one, no
expectation is set, but since I''m using a stubbed model, I
don''t want
the class actually calling real methods, hence the stub. I personally
find it''s easier and more logical to do it this way. Doing it like
this,
I could remove the first example and nothing else breaks and it still
makes perfectly good sense.

Peace,
Phillip