在对代码库进行单元测试时,有哪些明显的迹象表明我需要使用模拟对象?
这是否仅仅意味着在代码库中看到了许多调用其他对象的情况?
此外,如何对不返回值的方法进行单元测试?例如,如果一个方法返回void但是将内容打印到文件中,我只需检查文件的内容吗?
模拟是针对外部依赖项的,因此它涵盖了所有东西,包括文件系统、数据库、网络等等...
在对代码库进行单元测试时,有哪些明显的迹象表明我需要使用模拟对象?
这是否仅仅意味着在代码库中看到了许多调用其他对象的情况?
此外,如何对不返回值的方法进行单元测试?例如,如果一个方法返回void但是将内容打印到文件中,我只需检查文件的内容吗?
模拟是针对外部依赖项的,因此它涵盖了所有东西,包括文件系统、数据库、网络等等...
如果说有什么的话,我可能会过度使用模拟。
每当一个类调用另一个类时,通常我都会将该调用模拟出来,并验证该调用是否使用了正确的参数。在其他地方,我会编写单元测试来检查被模拟对象的具体代码是否正确执行。
例如:
[Test]
public void FooMoo_callsBarBaz_whenXisGreaterThan5()
{
int TEST_DATA = 6;
var bar = new Mock<Bar>();
bar.Setup(x => x.Baz(It.Is<int>(i == TEST_DATA)))
.Verifiable();
var foo = new Foo(bar.Object);
foo.moo(TEST_DATA);
bar.Verify();
}
...
[Test]
public void BarBaz_doesSomething_whenCalled()
{
// another test
}
对我来说,如果我试图将许多类作为一个大的整体进行测试,那么通常会有大量的设置代码。不仅阅读起来很困惑,因为您需要理解所有依赖项,而且在需要进行更改时非常脆弱。
我更喜欢简洁明了的小测试。编写、维护和理解测试意图都更容易。
在单元测试中,模拟/存根/伪造/测试替身等是可以的,并且允许对类/系统进行孤立测试。集成测试可能不使用任何模拟; 它们实际上会影响数据库或其他外部依赖项。
当你必须使用模拟或存根时,你就需要这样做。通常情况下,这是因为你尝试测试的类具有接口依赖性。对于 TDD,您希望按接口而非实现编程,并使用依赖注入(一般而言)。
一个非常简单的例子:
public class ClassToTest
{
public ClassToTest(IDependency dependency)
{
_dependency = dependency;
}
public bool MethodToTest()
{
return _dependency.DoSomething();
}
}
IDependency是一个接口,可能包含昂贵的调用(数据库访问、Web服务调用等)。测试方法可能包含类似以下代码:
// Arrange
var mock = new Mock<IDependency>();
mock.Setup(x => x.DoSomething()).Returns(true);
var systemUnderTest = new ClassToTest(mock.Object);
// Act
bool result = systemUnderTest.MethodToTest();
// Assert
Assert.That(result, Is.True);
单元测试仅适用于在其内部自主工作的一段代码。这意味着它不依赖于其他对象来完成工作。如果您正在进行测试驱动编程或测试优先编程,则应使用模拟。您将创建一个函数的模拟(或存根,我喜欢这样称呼它),并设置某些条件以使测试通过。最初,该函数返回false并且测试失败,这是预期的...然后您编写代码来执行实际工作,直到测试通过。
但我认为您所指的是集成测试,而不是单元测试。在这种情况下,如果您正在等待其他程序员完成他们的工作,并且当前无法访问他们创建的函数或对象,则应使用模拟。如果您知道接口,希望您确实知道,否则模拟就毫无意义,浪费时间,那么您可以创建一个简化版本,以期望未来获得的内容。
简而言之,当您在等待他人并需要某些东西才能完成工作时,最好利用模拟。
如果可能,您应始终返回值。有时会遇到问题,您已经返回了某些内容,但在C和C ++中,您可以具有输出参数,然后使用返回值进行错误检查。