【问题标题】:Should I unit-test with data that should not be passed in a function (invalid input)?我应该使用不应在函数中传递的数据(无效输入)进行单元测试吗?
【发布时间】:2011-09-07 17:55:27
【问题描述】:

我正在尝试使用 TDD 进行编码练习。我想问我是否应该使用函数中不应该发生的数据进行测试,但这些数据可能会破坏您的程序。

这是一个简单的例子来说明我的要求:

具有一个 INT 参数的 ROBOT 函数。在此函数中,我知道有效范围仅为 0-100。如果使用-1, 101,函数会中断。

function ROBOT (int num){
...
...
...
return result;
}

所以我决定为这个功能做一些自动化测试用例...

1. function ROBOT with input argument 0
2. function ROBOT with input argument 1
3. function ROBOT with input argument 10
4. function ROBOT with input argument 100

但是我应该为这个 ROBOT 函数编写带有输入参数 -1 还是 101 的测试用例,如果我会在我的另一个调用函数 ROBOT 的函数中保护它???

5. function ROBOT with input argument -1
6. function ROBOT with input argument 101

我不知道是否有必要,因为我认为测试-1和101是多余的。如果真的需要覆盖所有情况,我必须编写更多代码来保护-1和101。

那么在TDD的通用实践中,你会在-1和101上写测试用例吗???

【问题讨论】:

  • 与其说是 TDD 的常见做法,但定义了特定的功能测试技术来处理范围和边界,例如您的示例中的情况。详情见我的回答。
  • 您的语言是否有可访问性修饰符?如果ROBOT() 是私有的,你不应该测试它,你应该只测试公共函数/方法。
  • 我对标题做了一些改动,因为这个问题的答案对所有测试方法都有用,而不仅仅是 TDD。如果不准确请回滚。

标签: unit-testing testing tdd


【解决方案1】:

是的,您应该测试那些无效的输入。但是,如果您的语言具有可访问性修饰符并且 ROBOT() 是私有的,则您不应该对其进行测试;你应该只测试公共函数/方法。


功能测试技术称为边界值分析

如果你的范围是 0-100,你的边界值是 0 和 100。你应该测试,至少

  • 低于边界值
  • 边界值
  • 高于边界值

在这种情况下:

-1,0,1,
99,100,101

您假设 -1 到 -infinity 以下的所有内容都表现相同,1-99 之间的所有内容表现相同,101 以上的所有内容表现相同。这称为等价分区。边界值之外和之间的范围称为分区,您假设它们将具有相同的行为。

您应该始终考虑使用 -1 作为测试用例,以确保在参数不是强类型的情况下,负数和文本字符串不会发生任何有趣的事情。

【讨论】:

  • +1 表示 BVA 提及。 :) 此外,您应该检查常用的“特殊值”,即使它们不是 BV 的一部分。例子当然是 0 和 -1。此外,一些(非常旧的)遗留系统使用 999。您还应该添加正在使用的数据类型的最大值和最小值(例如 int.MaxValue,在 .NET 中)。
【解决方案2】:

如果预期结果是使用无效输入值引发异常,则适当地测试是否正确引发了异常。

编辑:

正如我在下面的评论中所指出的,如果这些情况会破坏您的应用程序,您应该抛出异常。如果这些情况在逻辑上确实不可能发生,那么我会说不,你不需要抛出异常,也不需要测试用例来覆盖它。

请注意,如果您的系统组件化得很好,并且此功能是一个组件,那么逻辑上不可能现在这一事实并不意味着它总是逻辑上不可能。以后可能会以不同的方式使用它。

【讨论】:

  • 我已经更正了,如果它只是破坏代码并且不会引发异常怎么办? (我已经修改了我的问题):p
  • 如果它破坏了应用程序,它应该抛出一个异常!
【解决方案3】:

简而言之,如果它可以打破,那么你应该测试它。还要尽早验证数据。

答案取决于您是否控制传递给 Robot 的输入。如果 Robot 是一个内部类 (C#) ;值仅从作为公共类型的 RobotClientX 流入。然后我将警卫检查放在 RobotClientX 中,为它编写测试。我不会为 Robot 编写测试,因为无效值不能在两者之间实现。 例如如果我将验证放在 GUI 中,以便在源头过滤掉所有无效值,那么我不会检查 GUI 下所有类中的无效值(除非我还公开了一个绕过 GUI 的公共 API) .

另一方面,如果 Robot 是公开可见的,即任何人都可以使用他们喜欢的任何值调用 Robot,那么我需要测试来记录它在给定特定类型的输入时的行为.. 作为其中之一无效。例如如果你传递一个超出范围的值,它会抛出一个 ArgumentException。

【讨论】:

    【解决方案4】:

    您说过如果参数无效,您的方法将引发异常。

    所以,是的,您应该这样做,因为您应该测试是否引发了异常。

    【讨论】:

    • 好吧,如果不是呢?它只会破坏程序?
    • 这种测试有点艺术形式。我会测试它,只是因为它很容易做到并且它使您的测试更加完整。如果无效输入的行为未定义,那么您可以将参数设为不测试。
    • @hvgotcodes,谢天谢地,这种测试已经变得科学而不是巧妙。有 2 种定义的技术来获得最少的测试用例。 +1 明确表示您无法测试未定义的行为,但我不会提出不测试无效输入的论点,应定义此行为并显示不完整的规范(如果不是)。
    【解决方案5】:

    如果其他代码防止错误地调用该方法,并且没有其他人会编写代码来调用该方法,那么我认为没有理由使用无效值进行测试。对我来说,这似乎是浪费时间。

    【讨论】:

    • 由于您不能保证有人不会错误地调用ROBOT(),并且对于无效输入有定义的预期行为,因此应该对其进行测试。除非它是私人的。
    【解决方案6】:

    契约式编程风格的设计和实现引起了人们对这样一个事实的注意:一个函数(方法)应该只负责一些事情,而不是所有事情。它调用(委托给)和调用它的其他函数也有责任。这种职责划分是将编程任务划分为可以单独执行的较小任务的核心。 契约 契约式编程的部分是函数的规范说明函数必须做什么当且仅当函数的调用者履行赋予调用者的职责按该规范。输入整数在[0,100]范围内的要求就是这种要求。

    现在,单元测试不应该测试实现细节。他们应该测试该功能是否符合其规范。这使得实现可以在不中断测试的情况下进行更改。它使重构成为可能。

    结合这两个想法,我们如何为一个给定特定 invalid 输入的函数编写测试?我们应该检查函数的行为是否符合规范。但是规范没有说明在这种情况下函数必须做什么。所以我们不能在无效函数调用后编写任何程序状态检查;行为是未定义。所以我们根本不能写这样的测试。

    【讨论】:

      【解决方案7】:

      我的回答是,不,您不希望出现异常,您不希望让ROBOT() 检查超出范围的输入。客户端应该表现得非常好,以至于他们不会传入垃圾值。

      您可能想要记录这一点 - 只是说客户必须小心他们传入的值。

      除了你要从哪里得到无效值之外?好吧,用户输入或通过将字符串转换为数字。但在这些情况下,应该是转换例程执行检查并提供有关值是否有效的反馈。应该保证这些值在接近ROBOT() 之前很久就有效!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-07-16
        • 2011-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-19
        • 1970-01-01
        • 2011-06-07
        相关资源
        最近更新 更多