A MockExpectationError problem

Hi everyone,

I have a test for importing vcards, and I was finding that each time it
was
being run, it was generating new photos and since it was using the test
database, it was actually overwriting real photos that were being stored
in the
file system (they are named based on the record’s id). So it was
apparent to
me that I need to use mocking to intercept Photo.create so that it
doesn’t
really happen…

I tried to do this, but I am getting this MockExpectationError, which I
don’t quite understand the meaning of :[]=


The relationships are:

Photo belongs_to :contact
Contact has_one :photo


this is how I am mocking in my test:

@photo = mock_model(Photo)
Photo.should_receive(:create).and_return(@photo)


the relevant code that is getting called to create the photo:

def card_photo
@contact.photo = Photo.create(:image_file_string =>
@card.photos.first)
end


And… the error:
1)
Spec::Mocks::MockExpectationError in ‘Vcard importing a vcard a card
with an invalid address, a photo, birthday, url, phone, and email should
update an existing contact when an email is found’
Mock “Photo_1001” received unexpected message :[]= with (“contact_id”,
201)
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_proxy.rb:185:in
set_belongs_to_association_for' /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/has_one_association.rb:56:in replace’
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1281:in
photo=' /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_proxy.rb:217:in send’
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_proxy.rb:217:in
method_missing' /Users/patrick/coding/rails/xyz/app/models/vcard.rb:164:in card_photo’


Any ideas?

Thanks…

Patrick J. Collins
http://collinatorstudios.com

On 2010-04-25 3:43 AM, Patrick J. Collins wrote:

Hi everyone,

I have a test for importing vcards, and I was finding that each time it was
being run, it was generating new photos and since it was using the test
database, it was actually overwriting real photos that were being stored in the
file system (they are named based on the record’s id). So it was apparent to
me that I need to use mocking to intercept Photo.create so that it doesn’t
really happen…

…snip…

Any ideas?

Thanks…

Patrick J. Collins
http://collinatorstudios.com

Hi Patrick,

See medium_spec.rb · GitHub 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.

Here’s a little info on the app to shed some light on what I’m doing in
the spec. I am working on an app that has a “media library” that users
can upload into. There are four types of media: audio, video, images,
and documents. During the upload process, I try to determine the mime
type automatically, and from that, the type of media, but I do allow
those things to be set manually. I have a run-time constant
(APP_CONFIG[:media_root_path]) that defines where the files should be on
the file system, and I have to change that during the test to make sure
development files don’t get overwritten. That’s accomplished with the
call to set_app_config, which is really nothing more than replacing a
constant. Its definition is

 def set_app_config(new_app_config)
     saved_app_config = APP_CONFIG.dup
     Kernel::silence_warnings { Object.const_set(:APP_CONFIG,

new_app_config ) }
return saved_app_config
end

Let me know if you have any questions.

Peace,
Phillip

On 2010-04-25 4:25 PM, Patrick J. Collins wrote:

On Sun, 25 Apr 2010, Phillip K. wrote:

See medium_spec.rb · GitHub 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.

behavior to stop… Just wondered if this is normal?

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

do
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

Here is a gist of my code:

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

Well, as you can see by my code, I am actually doing a loop

@card.addresses.each … This is calling the @country = get_country ||
Country.find_by_name(“United States”)

So-- the number of times .find_by_name needs to be intercepted by
.should_receive is dependent on the number of addresses in a vcard.

This was the problem I didn’t know how to address, and using stub!
resolved it,
but like I said-- I didn’t know if there’s a more appropriate way to do
it.

Patrick J. Collins
http://collinatorstudios.com

On 2010-04-25 5:25 PM, Patrick J. Collins wrote:

Country.find_by_name(“United States”)

So-- the number of times .find_by_name needs to be intercepted by
.should_receive is dependent on the number of addresses in a vcard.

And this would be the difference between stubbing and expecting. Do you
need to set an expectation that it gets called a certain number of
times? If not, just stub it (which is what you did and the error went
away). Only use should_receive when you want to verify that a method is
actually being called in your code. I generally do that when I want to
make sure different branches of execution are followed correctly.

This was the problem I didn’t know how to address, and using stub! resolved it,
but like I said-- I didn’t know if there’s a more appropriate way to do it.

Patrick J. Collins
http://collinatorstudios.com

Regarding your original problem of the mocking error, have you debugged
into the FlexImage code? In looking at your gist, you have this:

     # @photo = mock_model(Photo, {:image_file_string => 

photo_content})

which says “create a mock object based on the Photo model, and if
#image_file_string gets called on that object, return photo_content”.
Then you have

     # Photo.should_receive(:create).and_return(@photo)

which obviously says “Photo should receive a call to the .create method,
and when it does, return @photo”. The potential problem that I see
(though I may not be understanding something correctly), is when you
call Photo.create, .image_file_string= is being called in the FlexImage
codebase. I’m wondering if your issue might be somewhere down in there.
I’d do some more digging, but I have to run right now.

Good luck!

Peace,
Phillip

On Sun, 25 Apr 2010, Phillip K. wrote:

See medium_spec.rb · GitHub 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 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.


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?

  1. 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.

Patrick J. Collins
http://collinatorstudios.com

Regarding your original problem of the mocking error, have you debugged into
the FlexImage code? In looking at your gist, you have this:

    # @photo = mock_model(Photo, {:image_file_string => photo_content})

which says “create a mock object based on the Photo model, and if
#image_file_string gets called on that object, return photo_content”. Then you
have

    # Photo.should_receive(:create).and_return(@photo)

… Well, from what I can tell, the problem is not with
Photo.should_receive… If I change

@contact.photo = Photo.create(…)

to

Photo.create(…)

then the spec passes… It’s when that @contact.photo = is there that
it
breaks.

Patrick J. Collins
http://collinatorstudios.com