我刚开始接受我的第一个model spec
任务。在编写了很多特性规范之后,我发现很难进入编写模型规范的不同视角(不考虑上下文)。我将以Order
模型的方法为例,说明我遇到的困难:
def update_order_prices
self.shipping_price_cents = SHIPPING_PRICE_CENTS unless shipping_price_cents
return if order_lines.empty?
self.total_price_cents = calculate_order_price
self.total_line_items_price_cents = calculate_total_order_line_price
self.total_tax_cents = calculate_tax_amount
end
EDIT TL; DR
我完全满意的答案只是为我写了一个这个方法的规范。这篇文章的其余部分只是展示了我到目前为止的尝试,但没有必要回答这个问题。
第一种方法:
起初我不知道要测试什么。我试图找出方法被调用的时间和地点,并找到一个方案,我将知道这个方法中涉及的属性应该等于什么。简而言之,我花了很多时间试图理解上下文。然后一位同事说我应该在模型规范中测试自包含的方法,独立于上下文。我应该确保我确定所有案件。所以这个方法应该是:
目前的做法:
我尝试为这些要点编写测试,但仍然会出现问题:
测试1
it 'sets shipping price cents to default (if not done already)' do
order.shipping_price_cents = nil
order.update_order_prices
expect(order.shipping_price_cents).to eq(Order::SHIPPING_PRICE_CENTS)
end
我有信心我把这个弄好了,但我可以随意证明我的错。我将shipping_price_cents设置为nil以触发设置它的代码,将美分上的测试方法调用为等于模型中定义的默认值。
测试2
it 'returns early if order_lines is empty' do
expect(order.update_order_prices).to eq(nil)
end
所以在这里我想测试一下,当order_lines
关联中没有对象时,该方法会提前返回。我没有线索如何做到这一点所以我进入控制台,接受订单,删除与之关联的order_lines,并调用方法以查看将返回的内容。
2.3.1 :011 > o.order_lines
=> #<ActiveRecord::Associations::CollectionProxy []>
2.3.1 :012 > o.update_order_prices
=> nil
然后对具有关联order_line的订单执行相同的操作:
2.3.1 :017 > o.update_order_prices
=> 1661
所以我测试了'nil&#39;被退回。但它并不觉得我正在测试正确的事情。
测试3
it 'sets (the correct?) values if order_line is set' do
order_line = create(:order_line, product: product)
order = create(:order, order_lines: [order_line])
order.update_order_prices
expect(order.total_price_cents).to eq(order.calculate_order_price)
expect(order.total_line_items_price_cents).to eq(order.calculate_order_line_price)
expect(order.total_tax_cents).to eq(order.calculate_tax_amount)
end
我只是测试属性与它们设置的相等,而不使用实际值,因为我不应该向外看。如果我想测试绝对值,我将不得不在此函数之外进行调查,然后不会测试该方法,还会测试Order对象的状态等。
运行测试
Failures:
1) Order Methods: #update_order_prices sets (the correct?) values if order_line is set
Failure/Error: expect(order.total_price_cents).to eq(order.calculate_order_price)
NoMethodError:
private method `calculate_order_price' called for #<Order:0x007ff9ee643df0>
Did you mean? update_order_prices
所以,前两个测试通过,第三个测试没有。在这一点上,我感到有点失落,并希望听到一些有经验的开发人员如何编写这个看似简单的测试。
由于
答案 0 :(得分:1)
我猜你必须在update_order_prices
之后对你期望的确切值进行规范。
假设您设置订单和订单行的总价格为10欧元,那么我会添加以下期望
expect(order.total_price_cents).to eq(1000)
其他方法相同。通常我会尝试针对特定值进行测试。此外,当您依赖私有方法的结果时,您只关心结果。