如果函数只调用另一个函数或执行操作。我该如何测试?目前,我强制执行所有函数应返回一个值,以便我可以断言函数返回值。但是,我认为这种方法会在生产代码中大量使用API。我不需要那些函数来返回值。有什么好的解决方案吗?
我认为模拟对象可能是一种可能的解决方案。我想知道何时应该使用assert以及何时应该使用mock对象?有没有一般的指导方针?
谢谢
答案 0 :(得分:4)
让我们使用BufferedStream.Flush()作为不返回任何内容的示例方法;如果我们自己编写这个方法,我们将如何测试这个方法?
总有一些可观察效果,否则该方法将不存在。所以答案可以是测试效果:
[Test]
public void FlushWritesToUnderlyingStream()
{
var memory = new byte[10];
var memoryStream = new MemoryStream(memory);
var buffered = new BufferedStream(memoryStream);
buffered.Write(0xFF);
Assert.AreEqual(0x00, memory[0]); // not yet flushed, memory unchanged
buffered.Flush();
Assert.AreEqual(0xFF, memory[0]); // now it has changed
}
诀窍是构建代码,以便在测试中不太难以观察到这些影响:
BufferedStream
的编程方式
针对Stream
界面。这使得
您可以传递更简单,测试友好的实现(在本例中为MemoryStream
)或使用模拟框架(如MoQ或RhinoMocks),这对于单元测试非常有用。答案 1 :(得分:2)
是的,如果你想测试调用某个函数并传入某些参数,那么mock通常是要走的路。
以下是Typemock(C#)中的操作方法:
Isolate.Verify.WasCalledWithAnyArguments(()=> myInstance.WeatherService("","", null,0));
Isolate.Verify.WasCalledWithExactArguments(()=> myInstance. StockQuote("","", null,0));
通常,您应尽可能使用Assert
,直到您无法使用它为止(例如,当您必须测试是否正确调用外部Web服务API时,在这种情况下,您不能/不想直接与Web服务通信)。在这种情况下,您使用mock来验证是否使用正确的参数正确调用了某个Web服务方法。
答案 2 :(得分:2)
很抱歉没有直接回答,但是......你确定你的测试中有确切的平衡吗?
我想知道你是不是在测试太多? 你真的需要测试一个只委托给另一个人的函数吗?
当你写这篇文章时,我同意你的意见,你不想添加仅对测试有用的返回值,而不是生产。这会使您的API变得混乱,使其变得不那么清晰,这最终会带来巨大的成本。
此外,您的返回值对于测试似乎是正确的,但没有任何内容表示实现返回与实现相对应的返回值,因此测试可能无法证明任何。
请注意,测试具有初始成本,即编写测试的成本。 如果实施非常简单,那么失败的风险是非常低的,但是测试的时间仍然累积(超过一百或几千个案例,最终会非常严重)。
但更重要的是,每次重构生产代码时,您可能还需要重构测试。因此测试的维护成本会很高。
测试方法的作用(它调用的其他方法等)受到批评,就像测试私有方法一样......有几点要做:
答案 3 :(得分:1)
“我想知道何时应该使用断言,何时应该使用模拟对象?是否有任何一般指导原则?”
有一个绝对的,固定的和重要的规则。
您的测试必须包含断言。断言的存在是您用来查看测试是通过还是失败的内容。测试是一种方法,它调用特定夹具中的“被测组件”(一个函数,一个对象,等等),并对组件的行为进行特定的断言。
测试断言正在测试的组件。每个测试都必须有一个断言,或者它不是一个测试。如果它没有断言,则不清楚你在做什么。
模拟是组件的替代,以简化测试配置。它是替换真实组件的“模拟”或“模仿”或“虚假”组件。您可以使用模拟来替换某些内容并简化测试。
假设您要测试功能a。并且函数调用函数b。
函数a的测试必须有一个断言(或者它不是测试)。
a的测试可能需要模拟函数b。要隔离这两个函数,可以使用模拟函数b来测试a。
函数b的测试必须有一个断言(或者它不是测试)。
对b的测试可能不需要任何嘲弄。或者,也许b进行OS API调用。这可能需要嘲笑。或者也许b写入文件。这可能需要嘲笑。