Expect the creation of a new object

Ben Hawker picture Ben Hawker · Feb 1, 2016 · Viewed 7.5k times · Source

I need to write an expectation that a new object will be created in a payment system. Code included from order.rb and order_spec.rb (Order class is here):

#order.rb 

def confirm_order(method_of_payment)
  if credit_card_but_products_out_stock?(method_of_payment)
    raise "Cannot make credit card payment now, as some products are out of stock" 
  end

  order_total
  Payment.new(method_of_payment, self.total)
end

#order_spec.rb

it 'it creates a new payment object if method_of_payment is valid and order.total is > 0' do
  order.add_product(product, 3)
  order.confirm_order(:credit_card)
  #Expect that a new payment object is created.
end

I want to understand how I can write the appropriate spec to test that the new Payment object is created. I found this article from Semaphore CI useful, but am not sure about a solution. I am pretty sure I should be creating a test double of some sort, and then maybe a method stub to allow(order).to receive(:confirm_order).and_return(#new_object??).

Answer

maniacalrobot picture maniacalrobot · Feb 1, 2016

If you're unit testing the Order class, then you shouldn't be mocking or stubbing the Order class. You could set an expectation on the Payment class to return a mock that you can then further assert as the return value of confirm_order method.

let(:payment) { double :payment }

it "returns a payment" do
  expect(Payment).to receive(:new).with(:credit_card).and_return(payment)

  order.add_product(product, 3)
  expect(order.confirm_order(:credit_card)).to eq(payment)
end

This way, you are mocking the dependencies of your Order class, without altering the Order class, which is much safer as mocking method on Order could easily create false-positive test results.