Unit testing 随机数据的单元测试
我读过,在单元测试中生成随机数据通常是一个坏主意(我也理解为什么),但是在随机数据上进行测试,然后从随机测试中构建一个固定的单元测试用例,这样可以发现bug,这看起来不错。然而,我不明白如何很好地组织它。我的问题实际上与特定的编程语言或特定的单元测试框架无关,所以我将使用python和一些伪单元测试框架。以下是我对编码的看法:Unit testing 随机数据的单元测试,unit-testing,testing,tdd,Unit Testing,Testing,Tdd,我读过,在单元测试中生成随机数据通常是一个坏主意(我也理解为什么),但是在随机数据上进行测试,然后从随机测试中构建一个固定的单元测试用例,这样可以发现bug,这看起来不错。然而,我不明白如何很好地组织它。我的问题实际上与特定的编程语言或特定的单元测试框架无关,所以我将使用python和一些伪单元测试框架。以下是我对编码的看法: def random_test_cases(): datasets = [ dataset1, dataset2, ...
def random_test_cases():
datasets = [
dataset1,
dataset2,
...
datasetn
]
for dataset in datasets:
assertTrue(...)
assertEquals(...)
assertRaises(...)
# and so on
问题是:当这个测试用例失败时,我无法找出是哪个数据集导致了失败。我认为有两种解决方法:
assertTrue(…,message=str(dataset))
)。问题是我应该将这样的消息传递给每个断言,这看起来并不优雅有更简单的方法吗?您可以通过扩展而不是枚举来定义测试,或者您可以从单个用例调用多个测试用例 从单个测试用例调用多个测试用例:
MyTest()
{
MyTest(1, "A")
MyTest(1, "B")
MyTest(2, "A")
MyTest(2, "B")
MyTest(3, "A")
MyTest(3, "B")
}
有时有一些优雅的方法可以通过一些测试框架来实现这一点。是如何在NUnit中实现的:
[Test, Combinatorial]
public void MyTest(
[Values(1,2,3)] int x,
[Values("A","B")] string s)
{
...
}
我仍然认为这是个坏主意 单元测试需要简单明了。给定相同的代码和相同的单元测试,您应该能够无限地运行它,并且永远不会得到不同的响应,除非有外部因素在起作用。与此相反的目标将增加自动化的维护成本,这与目的背道而驰 除了维护方面,对我来说,它似乎很懒惰。如果您考虑到您的功能并理解正面和负面的测试用例,那么开发单元测试是很简单的
我也不同意用户演示如何在同一个测试用例中执行多个测试用例。当测试失败时,您应该能够立即知道哪个测试失败,并知道失败的原因。测试应该尽可能简单,并且尽可能简洁/与被测代码相关。通常,只要选择正确的断言方法,单元测试框架就支持“信息性失败” 但是,如果其他一切都不起作用,您可以轻松地将数据集跟踪到控制台/输出文件。技术含量低,但应该有效
[TestCaseSource("GetDatasets")]
public Test.. (Dataset d)
{
Console.WriteLine(PrettyPrintDataset(d));
// proceed with checks
Console.WriteLine("Worked!");
}
我也认为这是个坏主意 请注意,不要向代码中抛出随机数据,而是让单元测试这样做。这一切都归结为为什么您首先要进行单元测试。答案是“推动代码的设计”。随机数据不会驱动代码的设计,因为它依赖于非常严格的公共接口。请注意,您可以通过它找到bug,但这不是单元测试的目的。让我注意到,我说的是单元测试,而不是一般的测试 话虽如此,我还是强烈建议大家看一看。这是Haskell,所以在演示和文档方面有点狡猾,但你应该能够弄清楚。不过,我要总结一下它是如何工作的 在选择要测试的代码(比如说
sort()
函数)之后,就建立了应该保存的不变量。在本例中,如果result=sort(input)
,则可以使用以下不变量:
结果中的每个元素应小于或等于下一个元素
中的每个元素应在输入
中出现相同的次数结果
和结果
应该具有相同的长度(这是重复前面的内容,但让我们用它来说明)输入
输入
。由于这是Haskell,并且类型系统非常棒,它可以看到函数接受一个整数列表,并且知道如何生成这些整数。它基本上生成随机整数和随机长度的随机列表。当然,如果您有一个更复杂的数据类型(例如,只有正整数,只有平方等),那么它可以更细粒度
最后,当你有这两个,你只需运行快速检查。它随机生成所有这些内容并检查不变量。如果有些失败了,它会告诉你到底是哪一个。它还将告诉您随机种子,因此如果需要,您可以重新运行此失败。作为额外的奖励,每当它得到一个失败的不变量时,它将尝试将输入减少到使不变量失败的最小可能子集(如果你想到一个树结构,它将把它减少到使不变量失败的最小子树)
就在这里。在我看来,这就是你应该如何用随机数据来测试东西。这绝对不是单元测试,我甚至认为您应该以不同的方式运行它(比如,让CI时不时地运行它,而不是每次更改都运行它(因为它会很快变慢))。让我重复一遍,单元测试的另一个好处是快速检查可以发现bug,而单元测试可以驱动设计。在R的快速检查中,我们试图解决这个问题,如下所示
- 测试实际上是伪随机的(种子是固定的),因此您可以随时复制测试结果(当然,排除外部因素)
函数返回足够的数据来重现错误,包括失败的断言和导致错误的数据。对test
的返回值调用一个方便的函数test
,将在失败断言开始时将您放入调试器,并将参数设置为失败的见证人。如果在批处理模式下执行测试,则等效repro