单元测试组合的数量惊人
The number of unit testing combinations is staggering
我是单元测试的新手(应届毕业生,现在在现实世界中)。我对编写测试需要多长时间感到困惑。
让我们举一个基本的例子。我写了一个像这样的函数:
def add(a, b)
return a + b
我想测试它的整数输入和无限精度的浮点数。例如,测试用例名称可能是 test_add_negative_integer_to_negative_integer.
假设
在边界处测试边缘情况代表所有其他情况。
Edges/Boundaries
数值类型 = {integer, float}
数值={负、零、正}
测试用例数
重复组合(假设参数顺序不重要)。
C(3 + 2 - 1, 2) * C(2 + 2 - 1, 1) = 18个测试用例满足假设条件。
将另一个值添加到数字类型集会产生 36 个不同的 个测试用例。
我是不是做错了什么?
单元测试是 writing/testing 代码的实用方法(TDD 是先编写单元测试以帮助您开发代码)。
您应该考虑的几件事:
只测试您的代码。测试其他人的API(在你的情况下,系统的API)就像你描述的那样是一个坑洞(它不再是单元测试)。这只需要一个测试:调用 add
时验证 +
被调用。
只测试可能损坏的部分。 Getters 和 setters 以及简单的包装器(就像你在这里)不太可能被你或你的一个队友破坏。错误出现在某些地方(即,圈复杂度、奇怪的耦合、大量改动的代码),这是您需要进行大部分测试的地方。
最后,如果您正在为系统 +
编写代码,您可能需要所有这 18 个测试,但可能不需要,因为整数具有一定的对称性。
记住,单元测试是为了帮助你开发和维护代码。如果没有帮助,请尝试少做。
我的单元测试代码通常是生产代码的 1.5 倍。
你的测试目标是什么?
它是否详尽地涵盖了所有可能的输入?如您所列,需要 18 个测试用例才能完全覆盖所有组合,但这些组合是否必要?
风险分析与确定代码测试的详尽程度密切相关。与此方法有缺陷相关的技术风险和业务风险是什么?如果它是一个关键风险,那么所有 18 种组合可能都是必要的。此外,某种 random testing 也很有帮助。
IMO,对于单元测试,我发现覆盖率是一个很好的指导指标,它提供了高度的置信度,同时避免了所有组合。如果您使用覆盖率作为目标,那么此方法将只需要一个单元测试。
如果这是一个关键功能,并且需要所有组合,我会推荐数据驱动的方法。这是一种指定所有 18 个测试用例并使用相同代码驱动每个测试的简洁方法。它解决了必须单独写 18 个 test_this_and_this
.
的问题
@parameterized([
(1, -1.0, 0),
(2, 3, 8),
(1, 9, 1),
(0, 9, 0),
])
def test_add(first, second, expected):
assert_equal(add(first, second), expected)
数据驱动测试也是一个很棒的组织工具。如果您发现自己与非技术测试人员一起工作,那么能够创建测试驱动程序非常强大,然后让非技术人员设计一个包含所有相关业务相关测试用例的 CSV,并将其作为数据驱动的输入提供测试。
我认为您问题的答案很简单:应用 TDD 过程,您会发现问题会自动消失。
更具体地说,您将从为 "add(a, b)" 功能编写您的第一个测试开始。对于 "add" 方法的初始空实现,这可以使用单行测试方法来完成。测试(比如 add(1, 2) == 3
)将按预期失败。
在第一个测试通过之后,您才会尝试编写第二个测试。
这里是此过程起作用的关键:第二个测试首先需要因正当理由而失败。 "valid reason" 将与当前 "add" 实施尚未满足的其他一些业务需求直接相关。如果找不到这样的需求,那就不会有二次测试了。
重复以上操作,直到无法添加新的测试为止。您会发现实现对所需功能的全面覆盖所需的测试数量要少得多。
我是单元测试的新手(应届毕业生,现在在现实世界中)。我对编写测试需要多长时间感到困惑。
让我们举一个基本的例子。我写了一个像这样的函数:
def add(a, b)
return a + b
我想测试它的整数输入和无限精度的浮点数。例如,测试用例名称可能是 test_add_negative_integer_to_negative_integer.
假设
在边界处测试边缘情况代表所有其他情况。
Edges/Boundaries
数值类型 = {integer, float}
数值={负、零、正}
测试用例数
重复组合(假设参数顺序不重要)。
C(3 + 2 - 1, 2) * C(2 + 2 - 1, 1) = 18个测试用例满足假设条件。
将另一个值添加到数字类型集会产生 36 个不同的 个测试用例。
我是不是做错了什么?
单元测试是 writing/testing 代码的实用方法(TDD 是先编写单元测试以帮助您开发代码)。
您应该考虑的几件事:
只测试您的代码。测试其他人的API(在你的情况下,系统的API)就像你描述的那样是一个坑洞(它不再是单元测试)。这只需要一个测试:调用
add
时验证+
被调用。只测试可能损坏的部分。 Getters 和 setters 以及简单的包装器(就像你在这里)不太可能被你或你的一个队友破坏。错误出现在某些地方(即,圈复杂度、奇怪的耦合、大量改动的代码),这是您需要进行大部分测试的地方。
最后,如果您正在为系统
+
编写代码,您可能需要所有这 18 个测试,但可能不需要,因为整数具有一定的对称性。
记住,单元测试是为了帮助你开发和维护代码。如果没有帮助,请尝试少做。
我的单元测试代码通常是生产代码的 1.5 倍。
你的测试目标是什么?
它是否详尽地涵盖了所有可能的输入?如您所列,需要 18 个测试用例才能完全覆盖所有组合,但这些组合是否必要?
风险分析与确定代码测试的详尽程度密切相关。与此方法有缺陷相关的技术风险和业务风险是什么?如果它是一个关键风险,那么所有 18 种组合可能都是必要的。此外,某种 random testing 也很有帮助。
IMO,对于单元测试,我发现覆盖率是一个很好的指导指标,它提供了高度的置信度,同时避免了所有组合。如果您使用覆盖率作为目标,那么此方法将只需要一个单元测试。
如果这是一个关键功能,并且需要所有组合,我会推荐数据驱动的方法。这是一种指定所有 18 个测试用例并使用相同代码驱动每个测试的简洁方法。它解决了必须单独写 18 个 test_this_and_this
.
@parameterized([
(1, -1.0, 0),
(2, 3, 8),
(1, 9, 1),
(0, 9, 0),
])
def test_add(first, second, expected):
assert_equal(add(first, second), expected)
数据驱动测试也是一个很棒的组织工具。如果您发现自己与非技术测试人员一起工作,那么能够创建测试驱动程序非常强大,然后让非技术人员设计一个包含所有相关业务相关测试用例的 CSV,并将其作为数据驱动的输入提供测试。
我认为您问题的答案很简单:应用 TDD 过程,您会发现问题会自动消失。
更具体地说,您将从为 "add(a, b)" 功能编写您的第一个测试开始。对于 "add" 方法的初始空实现,这可以使用单行测试方法来完成。测试(比如 add(1, 2) == 3
)将按预期失败。
在第一个测试通过之后,您才会尝试编写第二个测试。
这里是此过程起作用的关键:第二个测试首先需要因正当理由而失败。 "valid reason" 将与当前 "add" 实施尚未满足的其他一些业务需求直接相关。如果找不到这样的需求,那就不会有二次测试了。
重复以上操作,直到无法添加新的测试为止。您会发现实现对所需功能的全面覆盖所需的测试数量要少得多。