【问题标题】:Am I abusing stubbing in my unit tests?我在单元测试中滥用存根吗?
【发布时间】:2014-03-31 06:49:46
【问题描述】:

我正在开发一个 Ruby on Rails 应用程序,我有一个包含以下方法的 BillingPlan 模型:

class BillingPlan < ActiveRecord::Base
  # ...

  def billing_months
    dates = [Date.new(Date.today.year, start_month.value, billing_day)]

    while dates.size < billings_in_year
      dates << dates.last + recurrence.value.months
    end

    dates.map{ |d| d.month }
  end

  def billings_in_year
    12 / recurrence.value
  end
end

为了测试代码,我编写了以下规范:

describe BillingPlan do
  # ...

  describe '#billings_in_year' do
    subject do
      (plan = BillingPlan.new).stubs(
        recurrence: stub(value: 4)
      ) && plan
    end

    it 'returns the number of billings in a year' do
      expect(subject.billings_in_year).to eq(3)
    end
  end

  describe '#billing_months' do
    subject do
      (plan = BillingPlan.new).stubs(
        recurrence: stub(value: 2),
        start_month: stub(value: 2),
        billings_in_year: 6,
        billing_day: 21
      ) && plan
    end

    it 'returns the months when billing is done' do
      expect(subject.billing_months).to eq([2, 4, 6, 8, 10, 12])
    end
  end
end

如您所见,我已经设法通过使用 Mocha 完全隔离了这两种方法。但是,我想知道我是否在测试中滥用存根?是不是太依赖于方法的内部运作?您能否提供一个通用规则来了解一个人应该在多大程度上依赖模拟/伪造?

注意:recurrencestart_month 是第三方类的对象。

【问题讨论】:

    标签: ruby unit-testing tdd stubbing


    【解决方案1】:

    您的测试存根隔离他们测试的案例所需的内容,但它们确实暗示了您的方法存在的问题 - 它们不遵守Law of Demeter

    • 你可以自己玩。
    • 您可以玩自己的玩具(但不能将它们拆开),
    • 您可以玩给您的玩具。
    • 您还可以玩自己制作的玩具。

    每个地方都需要存根存根的值 - 你有问题。

    假设这是一个 Rails 程序,更改 满足法律的代码。首先,我们对 用户类:

    class User
      delegate :name, :to => :department, :prefix => true, :allow_nil => true
      # ...
    end
    

    如果由于某种原因此解决方案不可行,您可以使用此解决方案:

    Demeter 不会阻止我们第二次与对象交互- 和三阶关联;它只是断言我们不能交互 所有这些对象都在同一个方法中。再看一遍 法律的制定:

    ...M 向其发送消息的所有对象...

    Demeter 只是关于方法的规则;它不限制集合 类可以与之交互的类型。

    所以这是完全合法的:

    class StatPresenter
      def human_stats(human)
        "Age: #{human.age}.nCountry stats:n#{country_stats(human.country)}"
      end
    
      def country_stats(country)
        "  Mortality rate: #{country.mortality_rate}"
      end
    end
    

    【讨论】:

    • 在这种特殊情况下,我可以使用recurrence_valuestart_month_value,它们是自动添加的别名。非常感谢你让我想起了得墨忒耳定律:我记得前段时间读过它,但直到现在我才遵循它。
    猜你喜欢
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 2014-05-15
    • 1970-01-01
    • 2019-07-29
    • 2011-06-07
    相关资源
    最近更新 更多