【问题标题】:Rails: How to write model spec for this method?Rails:如何为此方法编写模型规范?
【发布时间】: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


    【解决方案1】:

    我猜你必须在update_order_prices 之后指定你期望的确切值。

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

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

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

    【讨论】:

    • 那我不是在测试应用的状态吗?
    • 不,您只想指定使用您之前定义的前提条件调用的方法的正确结果。这意味着在您的应用程序中,可能与订单行相关联的产品成本为 100 欧元,但在您的(型号/单位)规范中,您只需设置 1 欧元并验证结果是否符合您的预期。 state 将使用集成规范进行测试。
    猜你喜欢
    • 1970-01-01
    • 2011-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    相关资源
    最近更新 更多