作为一名程序员,我完全认同“测试驱动开发”的理念,并会为我写的任何非平凡代码编写广泛的单元测试。有时候这条路可能是痛苦的(行为变化导致级联多个单元测试的更改;需要大量的支架),但总的来说,我拒绝在没有可运行测试的情况下进行编程,结果我的代码出现漏洞的概率也少得多。
最近,我一直在尝试Haskell及其测试库QuickCheck。与TDD非常不同的是,QuickCheck强调测试代码的不变性,也就是某些属性在所有输入(或实质子集)上都成立。举个例子:一个稳定的排序算法如果我们运行两次应该会给出相同的答案并且输出递增,应该是输入的置换等。然后,QuickCheck会生成各种随机数据以测试这些不变量。
至少对于纯函数(即没有副作用的函数-如果你正确地进行模拟,可以将脏函数转换为纯函数),似乎不变性测试可以替代单元测试并严格是其超集。每个单元测试包括一个输入和一个输出(在命令式编程语言中,“输出”不仅仅是函数的返回值,还包括任何更改的状态,但这可以被封装)。可以想象创建一个随机输入生成器,它足以涵盖您手动创建的所有单元测试输入(并且还会生成您没有想到的案例);如果您发现程序中有错误,源自于某些边界条件,则可以改进随机输入生成器,使其也生成该案例。那么,挑战在于是否可以为每个问题制定有用的不变量。我认为是可能的:一旦你有一个答案,看看它是否正确比起一开始计算答案要简单得多。思考不变量也比临时测试用例更有助于澄清复杂算法的规范,后者鼓励了对问题逐个案例进行思考的方式。您可以使用程序的先前版本作为模型实现,或者使用另一种语言编写的程序版本等。最终,您可以涵盖所有以前的测试案例,而无需显式编码输入或输出。
我疯了吗,还是我发现了什么?
最近,我一直在尝试Haskell及其测试库QuickCheck。与TDD非常不同的是,QuickCheck强调测试代码的不变性,也就是某些属性在所有输入(或实质子集)上都成立。举个例子:一个稳定的排序算法如果我们运行两次应该会给出相同的答案并且输出递增,应该是输入的置换等。然后,QuickCheck会生成各种随机数据以测试这些不变量。
至少对于纯函数(即没有副作用的函数-如果你正确地进行模拟,可以将脏函数转换为纯函数),似乎不变性测试可以替代单元测试并严格是其超集。每个单元测试包括一个输入和一个输出(在命令式编程语言中,“输出”不仅仅是函数的返回值,还包括任何更改的状态,但这可以被封装)。可以想象创建一个随机输入生成器,它足以涵盖您手动创建的所有单元测试输入(并且还会生成您没有想到的案例);如果您发现程序中有错误,源自于某些边界条件,则可以改进随机输入生成器,使其也生成该案例。那么,挑战在于是否可以为每个问题制定有用的不变量。我认为是可能的:一旦你有一个答案,看看它是否正确比起一开始计算答案要简单得多。思考不变量也比临时测试用例更有助于澄清复杂算法的规范,后者鼓励了对问题逐个案例进行思考的方式。您可以使用程序的先前版本作为模型实现,或者使用另一种语言编写的程序版本等。最终,您可以涵盖所有以前的测试案例,而无需显式编码输入或输出。
我疯了吗,还是我发现了什么?