使用动态语言创建模拟时如何检测类型错误?


10

在执行TDD时会出现问题。经过几次测试通过后,某些类/模块的返回类型发生了变化。在静态类型的编程语言中,如果在其他某个类的测试中使用了先前的模拟对象,并且未对其进行修改以反映类型更改,则将发生编译错误。

但是,对于动态语言,可能无法检测到返回类型的更改,并且其他类的测试仍将通过。当然,可能会有集成测试稍后会失败,但是单元测试会错误地通过。有什么办法可以避免这种情况?

用一个简单的示例(在某些组合语言上)更新...

版本1:

Calc = {
    doMultiply(x, y) {return x * y}
}
//.... more code ....

// On some faraway remote code on a different file
Rect = {
    computeArea(l, w) {return Calc.doMultipy(x*y)}
}

// test for Rect
testComputeArea() { 
    Calc = new Mock()
    Calc.expect(doMultiply, 2, 30) // where 2 is the arity
    assertEqual(30, computeArea)
}

现在,在版本2上:

// I change the return types. I also update the tests for Calc
Calc = {
    doMultiply(x, y) {return {result: (x * y), success:true}}
}

... Rect随后将在运行时引发异常,但测试仍将成功。


1
到目前为止,似乎缺少答案的问题是,问题不在于涉及变更class X的测试,而是测试的测试class Y取决于X并因此根据与生产中所使用的合同不同的合同进行测试。
Bart van Ingen Schenau 2013年

我刚刚就依赖注入在SO上问了这个问题。请参阅原因1:可以在运行时更改相关类(请考虑测试)。我们都有相同的心态,但缺乏很好的解释。
Scott Coates 2013年

我正在重新阅读您的问题,但对解释有些困惑。你能举个例子吗?
Scott Coates

Answers:


2

在某种程度上,这只是使用动态语言进行业务的成本的一部分。您将获得很大的灵活性,也就是“足够的绳索来挂自己”。小心点。

对我而言,该问题建议您使用与静态类型语言不同的重构技术。在静态语言中,您将部分替换返回类型,以便可以“依靠编译器”查找可能中断的地方。这是安全的,而且可能比修改返回类型更安全。

在动态语言中,您无法执行此操作,因此,就地修改返回类型而不是替换返回类型要安全得多。可能您需要通过将新类作为成员添加到其中来修改它,以用于需要它的类。


2

如果您的代码更改了并且您的测试仍然通过,则说明您的测试有问题(即您缺少断言),或者代码实际上没有更改。

那是什么意思 好吧,您的测试描述了代码部分之间的契约。如果合同是“返回值必须是可迭代的”,则将返回值从一个数组更改为一个列表实际上并不是合同的更改,因此不一定会触发测试失败。

为了避免遗漏断言,您可以使用代码覆盖率分析(它不会告诉您代码的哪些部分,但是肯定告诉您哪些部分绝对未被测试),循环复杂度和NPath复杂度等工具(这为您提供了达到完整的C1和C2代码覆盖率所需的断言数量的下限)和突变测试器(它们将代码中的突变注入,例如将true变成false,将负数变成正数,将对象变成null等,并检查是否使测试失败)。


+1测试的某些错误或代码实际上并未更改。经过几年的C ++ / Java,这花了我一段时间才能习惯。动态代码语言的各个部分之间的约定不应返回什么,而应返回返回的内容包含什么以及它可以做什么。
MrFox 2013年

@suslik:实际上,这与静态语言和动态语言无关,而与使用抽象数据类型的数据抽象与面向对象的数据抽象有关。只要模拟另一个对象,因为它的行为没有区别(即使它们是完全不同的类型和完全不同的类的实例)的能力为目的之一是根本,以面向对象的整体思路。换一种说法:如果两个对象的行为相同,则它们的类型相同,而不管它们的类说什么。换句话说,类不是类型。不幸的是,Java和C#弄错了。
约尔格W¯¯米塔格

假设模拟的单元已被100%覆盖,并且没有断言:那么,从“应该为foo返回空”到“应该为foo返回空字符串”这样的更细微的变化呢?如果有人更改了该单元及其测试,则某些消耗单元可能会中断,并且由于模拟,这是透明的。(并且不:在编写或使用模拟模块时,按照“合同”,没有必要将空字符串作为返回值来处理,因为它应该返回非空字符串或仅返回null;))。(只有在交互中对两个模块进行正确的集成测试才能发现这一点。)
最终尝试
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.