在RSpec中存储返回值 - 正确使用双精度和存根

时间:2016-02-03 04:50:18

标签: ruby rspec mocking stub

我遇到了困惑,将测试结合在一起并将其删除。我的问题是 - 在confirm_purchase_order中测试create_orderclass PurchaseOrder方法的最合适方法是什么?

我在代码中包含了相关代码:

class PurchaseOrder
  attr_reader :customer, :products

  def initialize(customer)
    @products = {}
    @customer = customer
  end

  ....some other methods

  def add_product(product, quantity = 1)
    @products[product] = (@products[product] ? @products[product] + quantity :  quantity )
    puts "You haved added #{quantity} #{product.title}'s to your purchase order"
  end

  def confirm_purchase_order
    purchase_order_total
    raise "Your PO appears to be empty! Add some products and try again." unless self.total.to_f.round(2) > 0

    create_order
    create_invoice

    return "We have generated an Invoice and created an order."
  end

  def create_order
    order = Order.new(customer)
    order.products = @products.clone
  end

  def create_invoice
    invoice = Invoice.new(customer)
    invoice.products = @products.clone
  end
end
class Order
  attr_reader :customer
  attr_accessor :status, :total, :products

  def initialize(customer)
    @products = {}
    @status = :pending
    @customer = customer
  end
class Customer
  attr_reader :name, :type

  def initialize(name, type)
    @name = name.to_s
    @type = type.to_sym
  end
class Invoice

  attr_reader :customer, :products
  attr_accessor :total

  def initialize(customer, products)
    @products = {}
    @customer = customer
    @payment_recieved = false
  end
end

我想测试confirm_purchase_order方法以及create_order中的class PurchaseOrder方法。到目前为止我的方法:

我需要一些对象双打和实际的PurchaseOrder object

describe PurchaseOrder do
  let(:product) { double :product, title: "guitar", price: 5 }
  let(:order) { instance_double(Order) }
  let(:customer) { double :customer, name: "Bob", type: :company }
  let(:products) { {:product => 1} }

  let(:purchase_order) { PurchaseOrder.new(customer) }

  describe "#create_order" do

    it "returns an order" do
      expect(Order).to receive(:new).with(customer).and_return(order)
      allow(order).to receive(products).and_return(???products??!)

      purchase_order.add_product(product, 1)
      purchase_order.create_order
      expect(order.products).to eq (products)
    end
  end
end

我还看了一下使用:

 # order.stub(:products).and_return(products_hash)
 # allow_any_instance_of(Order).to receive(:products) { products_hash }
 # order.should_receive(:products).and_return(products_hash)

设置订单double以在调用order.products时返回产品哈希值,但这些都感觉他们过多地“操纵”测试。在confirm_purchase_order中测试create_orderclass PurchaseOrder方法的最合适方法是什么?

1 个答案:

答案 0 :(得分:1)

在我看来,也许你给了PurchaseOrder过多的责任。它现在对OrderInvoice有深入的了解。

我可能会测试当前的实现:

it "returns an order with the same products" do
  expect_any_instance_of(Order).to receive(:products=).with(products: 1)

  purchase_order.add_product(product, 1)
  expect(purchase_order.create_order).to be_a(Order)
end

但也许有必要将PurchaseOrderOrderInvoice分开,并做一些这样的事情:

class Invoice
  def self.from_purchase_order(purchase_order)
    new(purchase_order.customer, purchase_order.products.clone)
  end
end

class Order
  def self.from_purchase_order(purchase_order)
    new.tap(purchase_order.customer) do |invoice|
      invoice.products = purchase_order.products.clone
    end
  end
end

class PurchaseOrder
  # ...
  def create_order
    Order.from_purchase_order(self)
  end

  def create_invoice
    Invoice.from_purchase_order(self)
  end
end

describe PurchaseOrder do
  let(:customer) { double('a customer')}
  let(:purchase_order) { PurchaseOrder.new(customer) }
  describe "#create_order" do
    expect(Order).to receive(:from_purchase_order).with(purchase_order)
    purchase_order.create_order
  end

  describe "#create_invoice" do
    expect(Order).to receive(:from_purchase_order).with(purchase_order)
    purchase_order.create_order
  end
end

describe Order do
  describe '.from_purchase_order' do
    # test this
  end
end

describe Order do
  describe '.from_purchase_order' do
    # test this
  end
end

这样,您就可以让OrderInvoice类知道如何从PurchaseOrder构建自己。您可以单独测试这些类方法。 create_ordercreate_invoice的测试变得更加简单。

我想到的其他一些事情:

对于products,请尝试使用带有默认过程的哈希:

@products = Hash.new { |hash, unknown_key| hash[unknown_key] = 0 }

这样,您可以随时安全地执行@products[product] += 1