在 RSpec 中添加 return 值 - 正确使用双打和存根
Stubbing a return value in RSpec - correct usage of doubles and stubs
我 运行 混淆了将测试替身捆绑在一起并对其进行存根。我的问题是 - 在 class PurchaseOrder
中测试 confirm_purchase_order
和 create_order
方法的最合适方法是什么?
我已经包含了以下代码的相关代码:
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
方法以及 class PurchaseOrder
中的 create_order
方法。到目前为止我的方法:
我需要一些对象双打和一个实际的 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)
在调用 order.products
时将订单设置为 return 产品散列的两倍,但这些都感觉像是 'rigging' 测试太多了。在 class PurchaseOrder
中测试 confirm_purchase_order
和 create_order
方法的最合适方法是什么?
在我看来,您可能给了 PurchaseOrder
太多责任。它现在对 Order
和 Invoice
了如指掌。
我可能会像这样测试当前的实现:
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
但也许将 PurchaseOrder
与 Order
和 Invoice
稍微分离并做这样的事情可能是有意义的:
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
通过这种方式,您可以让 Order
和 Invoice
class 知道如何从 PurchaseOrder
构建自己。您可以单独测试这些 class 方法。 create_order
和 create_invoice
的测试变得更简单。
我想到的其他一些事情:
对于 products
,尝试使用具有默认过程的哈希:
@products = Hash.new { |hash, unknown_key| hash[unknown_key] = 0 }
通过这种方式,您可以随时安全地进行 @products[product] += 1
。
我 运行 混淆了将测试替身捆绑在一起并对其进行存根。我的问题是 - 在 class PurchaseOrder
中测试 confirm_purchase_order
和 create_order
方法的最合适方法是什么?
我已经包含了以下代码的相关代码:
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
方法以及 class PurchaseOrder
中的 create_order
方法。到目前为止我的方法:
我需要一些对象双打和一个实际的 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)
在调用 order.products
时将订单设置为 return 产品散列的两倍,但这些都感觉像是 'rigging' 测试太多了。在 class PurchaseOrder
中测试 confirm_purchase_order
和 create_order
方法的最合适方法是什么?
在我看来,您可能给了 PurchaseOrder
太多责任。它现在对 Order
和 Invoice
了如指掌。
我可能会像这样测试当前的实现:
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
但也许将 PurchaseOrder
与 Order
和 Invoice
稍微分离并做这样的事情可能是有意义的:
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
通过这种方式,您可以让 Order
和 Invoice
class 知道如何从 PurchaseOrder
构建自己。您可以单独测试这些 class 方法。 create_order
和 create_invoice
的测试变得更简单。
我想到的其他一些事情:
对于 products
,尝试使用具有默认过程的哈希:
@products = Hash.new { |hash, unknown_key| hash[unknown_key] = 0 }
通过这种方式,您可以随时安全地进行 @products[product] += 1
。