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:
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
#<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
.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.