Rails:如何编写此方法的模型规范?

时间:2017-02-09 10:52:38

标签: ruby-on-rails unit-testing rspec

我刚开始接受我的第一个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

我完全满意的答案只是为我写了一个这个方法的规范。这篇文章的其余部分只是展示了我到目前为止的尝试,但没有必要回答这个问题。

第一种方法:

起初我不知道要测试什么。我试图找出方法被调用的时间和地点,并找到一个方案,我将知道这个方法中涉及的属性应该等于什么。简而言之,我花了很多时间试图理解上下文。然后一位同事说我应该在模型规范中测试自包含的方法,独立于上下文。我应该确保我确定所有案件。所以这个方法应该是:

  • 它将运费价格美分设为默认值(如果尚未完成)
  • 如果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以触发设置它的代码,将美分上的测试方法调用为等于模型中定义的默认值。

测试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

所以,前两个测试通过,第三个测试没有。在这一点上,我感到有点失落,并希望听到一些有经验的开发人员如何编写这个看似简单的测试。

由于

1 个答案:

答案 0 :(得分:1)

我猜你必须在update_order_prices之后对你期望的确切值进行规范。

假设您设置订单和订单行的总价格为10欧元,那么我会添加以下期望

expect(order.total_price_cents).to eq(1000)

其他方法相同。通常我会尝试针对特定值进行测试。此外,当您依赖私有方法的结果时,您只关心结果。