【发布时间】:2017-02-09 10:52:37
【问题描述】:
我刚刚开始在工作中承担我的第一个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
编辑 TL;DR
我对一个简单地为我编写此方法规范的答案感到非常满意。这篇文章的其余部分只是展示了我到目前为止所做的尝试,但没有必要回答这个问题。
第一种方法:
起初我不知道要测试什么。我试图找出调用该方法的时间和地点,并找到一个场景,让我知道该方法中涉及的属性应该等于什么。简而言之,我花了很多时间试图理解上下文。然后一位同事说我应该在独立于上下文的模型规范中测试方法。我应该确保我确定所有案例。所以对于这个方法来说:
- 它将运费美分设置为默认值(如果尚未完成)
- 如果 order_lines 为空,则提前返回
- 如果设置了 order_line,它会设置值
目前的做法:
我尝试为这些点编写测试,但仍然出现问题:
测试 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 以触发设置它的代码,调用 cents 上的测试方法使其等于模型中定义的默认值。
测试 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”。但感觉我测试的东西不对。
测试 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
所以,前两个测试通过了,第三个没有。在这一点上,我感到有点失落,很想听听一些有经验的开发人员如何编写这个看似简单的测试。
谢谢
【问题讨论】:
标签: ruby-on-rails unit-testing rspec