【问题标题】:How can I avoid over-testing with example如何通过示例避免过度测试
【发布时间】:2013-05-01 04:24:00
【问题描述】:

我一直在尝试更多地测试我的代码,我想我会模仿一些由我自动生成的测试的风格 rails 脚手架(我正在使用 rspec 和 rails,但我的问题是真的只是一般-tdd)。但是,这些方法非常基本,类似于做某事。然后如果 foo,做其他事情。我觉得一旦您通过在 if 中添加另外 2 个条件来变得更加复杂,事情就会开始螺旋式上升,并且您会得到到处都是嵌套的示例。这是我一直在做的一个例子,感觉像是矫枉过正。

先方法

def maybe_blah(foo)
  if foo != nil && foos.find_by_id(foo.id) != nil
    blah unless bar?(foo)
  end
end

这个方法很简单,这就是我打算如何测试它

describe maybe_blah

  shared_examples "what happens when bad input is passed" do
    it "doesn't call bar?"...
    it "doesn't call blah"...
  end

  context "when foo is not nil"
    context "when foo is in foos"
      context "bar? returns true"
        it "doesn't call blah" do
           some code involving mocking bar? and blah 
        end
      end

      context "bar? returns false"
        it "does call blah" do
           some code involving mocking bar? and blah 
        end
      end
    end

    context "when foo is not in foos"
       include_examples "what happens when bad input is passed"
    end
  end

  context "when foo is nil"
     include_examples "what happens when bad input is passed"
  end
end

这明显比如果有所有设置和其他任何东西(测试 may_blah 像那样真实地测试我花了 55 行)的测试时间短,所以你可以看到它似乎是如何失控的。有没有一种很好的方法来测试一种确实感觉有点矫枉过正的方法。

当您有 3 个要测试的条件时(至少不重复自己更多),我看不出有 3 层深度嵌套的方法,但您似乎需要这样做以确保您'正在处理所有不同的案件。此外,为每个不同的错误输入测试失败结果似乎很愚蠢,但是你怎么知道你实际上在这些错误输入上失败了?

所以这只是矫枉过正吗?

【问题讨论】:

  • 我不确定那里的最佳实践是什么,但对我来说,你不应该编写只是重复实现的测试。当使用测试更容易指定预期行为时,您编写测试。如果通过实现更容易指定预期的行为,那就足够了。
  • 所以你的意思是不要测试 Maybe_blah?
  • 是的。这可能只是因为它是一个虚构的示例,但是除了实现本身之外,您似乎没有可以为该函数定义的任何合同,并且您要测试的是一个函数是否满足其合同。

标签: unit-testing testing rspec tdd


【解决方案1】:

其实你的条件是一样的:

def maybe_blah(foo)
  if foo != nil && foos.find_by_id(foo.id) != nil && !bar?(foo)
    blah
  end
end

因此,您可以使用Decompose ConditionalConsolidate Conditional Expression 技术将其提取到单独的方法中:

def maybe_blah(foo)
  blah if can_do_it?(foo)
end

def can_do_it?(foo)
  foo != nil && foos.find_by_id(foo.id) != nil && !bar?(foo)
end

之后你可以在两个contexts中测试这个方法

describe '#maybe_blah' do
  context 'when can do' do
    # stub can_do_it? and returns true
    # stould receive :blah
  end

  context 'when cant do' do
    # stub can_do_it? and returns false
    # should not receive :blah
  end
end

并分别测试条件。

你可以省略!= nil

def can_do_it?(foo)
  foo && foos.find_by_id(foo.id) && !bar?(foo)
end

【讨论】:

  • 这对于大型操作似乎不切实际。如果某样东西有一堆步骤,我不想把它分解成 20 种不同的方法。会很乱,很难维护
  • 这是一种典型的代码异味,因为这种复杂的情况很难一目了然。否则,您将无法避免在您的规范中进行深度嵌套。
  • 在实践中测试所有这些东西是否正常?我觉得大多数人都不会在他们编写的每一个方法上测试是否正确处理 nil。
猜你喜欢
  • 2019-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多