Entries Tagged 'association proxy' ↓

Mock Expectation Fails on Associated Model (Association Proxy)

I just hit a real mean snag today trying to put an expectation on a model and it didn’t get caught like it should. This is something I do all the time so I knew I wasn’t just making a basic typo. Here’s what my setup looked like:

The model

def purchase
  send_shipping_notice
end
  
def send_shipping_notice
  puts "hi"
end

In my spec/test

def given_bought_on(date)
  wp = @order.product
  puts wp.inspect
  wp.should_receive(:send_shipping_notice)
  wp.purchase
end

The output

#<Product id: 270, owner_id: 270, name: 'thing'>
hi

1)
Spec::Mocks::MockExpectationError in 'Product should purchase'
Mock 'Product' expected :send_shipping_notice with (any args) once, but received it 0 times
./spec/models/product_spec.rb:21:in `given_bought_on'
script/spec:4:

So as you can see, I put an expectation on the wp variable, and verify it gets called by seeing ‘Hi’ get printed. However the expectation totally gets overlooked and my test framework complains that it never happened.

The catch is that the wp in my test and the web_page self inside the model are two different models. So the expectation is on a different model. Calling .object_id or .inspect on either one will affirm (or rather lie) to you that they’re the same, but they are not.

This is (as I’m told) well documented in ActiveRecord and is intentional by design. The wp variable is in fact an Association Proxy, not the WebPage I think it is. It just lies to you outright so you think it’s a WebPage.

My solution, while rather a hack, is the only thing I know to get my spec to pass:

def given_bought_on(date)
  wp = Product.find(@order.product_id)
  puts wp.inspect
  wp.should_receive(:send_shipping_notice)
  wp.purchase
  @order.reload
end

Now it grabs the model directly and there’s no confusion.