UnitTesting帮助:使用存根?

时间:2016-11-30 16:46:48

标签: unit-testing

我有一个关于单元测试的问题。

我必须测试一个函数,将其称为函数A,它接收类B的实例作为输入,并返回true或false。

现在在我的测试中我必须以某种方式创建一个传递给函数A的对象;我目前正在做的是使用它的构造函数初始化类B并调用它的一些方法来填充它,创建正确的结构等,然后将它传递给函数A.现在,我不确定这个是一个很好的模式:特别是如果B类的方法中有一个错误,或者我改变它的界面会发生什么?

所以我想我需要使用存根;但是编写一个与我的B类结构基本相同的类似乎很奇怪;函数A最终将与B类一起使用,因此如果我更改了B类的接口,那么函数A的测试应该崩溃,告诉我更改函数A以适应新的接口。

使用的正确模式是什么?

注意:如果您认为这主要是基于意见,请将问题重新表述为:“根据”单元测试艺术“中提出的原则,这里最好的做法是什么?” - 对于其他理智的人,请随时以更大的视角撰写答案

修改

我应该澄清,功能A的唯一目的是采用B类的实例并验证是否满足某个条件。现在,我可以创建一个替代B类的存根,但我不确定这是否有意义;这看起来毫无意义。另一方面,为了初始化B我做了类似classB.addData(randomData);如果此代码失败会发生什么?我将在函数A的测试中得到一个错误,而实际的问题是在B类的初始化

编辑2 一些代码更明确地显示了函数的功能。真正的代码是完全相同的,除了方法更复杂,否则它是完全相同的

def functionA(objectB):
    return objectB.data < 10

def testFunctionA():
      objectB = classB()
      objectB.addData(19)      #Is this a problem or should I stub objectB?

      assert(functionA(objectB) is False)

3 个答案:

答案 0 :(得分:1)

在我看来,单元测试工具箱中最强大的工具是依赖注入,这完全适合这里。

构造对象时,其所有协作者对象应以接口引用的形式传递给其构造函数。在您的情况下,待测单元是一个功能,但原理是相同的。函数协作者对象以相同的方式传递给它。

如果测试对象仅通过接口与其他对象协作,则可以在单元测试中传递模拟对象。此外,谷歌模拟等框架使得创建模拟非常方便,并编写了清晰的测试用例,其中预期的交互很容易理解。

如果这是你的意思,那么是的,这是一个很好的模式。

编辑:

这就是我编写测试函数的方法。

def functionA(objectB):
    return objectB.data < 10

def testFunctionA():
      objectB = fakeClassB()
      EXPECT_CALL(objectB, data).WillOnce(Return(19))
      assert(functionA(objectB) is False)

传递一个fakeClassB对象,而不是传递一个真正的classB对象。这使得测试取决于classB的接口而不是classB的实际实现。失败的测试是由接口的错误使用引起的,而不是由classB的某些实现细节引起的。根据您使用的语言,我想这可能是否可能。

另一个好处是构建复杂性。您可以构建测试函数,而无需构建classB的实现。您只需要构建classB和fakeClassB的接口。

答案 1 :(得分:0)

评论太长了:

单元 测试A的范围内,B的实施应该无关紧要。使用mock / stub / what来测试A的不同分支是好的。

如果某个时刻B的实现发生了变化,那么A的实现就无关紧要了(理想情况下,但你的例子非常抽象)。如果B的实施发生了显着变化,A正在查看B的不同成员,那么是的,您可能会对A单位产生影响试验。如果B实现了A依赖于更改的成员,则对于B的模拟版本无关紧要。

然而B的单元测试很重要。

如果你有任何具体的内容我可以详细说明,但现在它主要只是一个抽象答案的抽象答案。

def functionA(objectB):
    return objectB.data < 10

def testFunctionA():
      objectB = classB()
      objectB.addData(19)      #Is this a problem or should I stub objectB?

      assert(functionA(objectB) is False)

是这个蟒蛇?我不知道python,但我猜测functionA在objectB.data为10时返回true,否则为false。

鉴于所有这些,functionA仍然只根据true的成员返回falseobjectBobjectB.data如何获取其数据与单元测试functionA的范围无关,因此objectB.data可以而且应该(如果可能)进行模拟/存根以获得真正的 单元测试 。集成测试是另一个故事(你应该使用真正的实现,但你的问题是特定于单元测试)

答案 2 :(得分:0)

测试中有一条规则:mock all units other than the tested one。模拟的目的是在调用tested单位中使用的单位时提供一些正确的输出值。您应该明确提供一些您知道正确的样本,以便tested单位不会因为它所使用的单位导致某些失败而失败。