我有一个关于单元测试的问题。
我必须测试一个函数,将其称为函数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)
答案 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
的成员返回false
或objectB
。 objectB.data
如何获取其数据与单元测试functionA
的范围无关,因此objectB.data
可以而且应该(如果可能)进行模拟/存根以获得真正的 单元测试 。集成测试是另一个故事(你应该使用真正的实现,但你的问题是特定于单元测试)
答案 2 :(得分:0)
测试中有一条规则:mock all units other than the tested one
。模拟的目的是在调用tested
单位中使用的单位时提供一些正确的输出值。您应该明确提供一些您知道正确的样本,以便tested
单位不会因为它所使用的单位导致某些失败而失败。