我刚开始阅读的是 专业的C#测试驱动开发:使用TDD开发实际应用程序
我很难理解存根(stubs),伪造对象(fakes)和模拟对象(mocks)。据我目前所了解的,它们都是用于单元测试项目的假对象,而模拟对象则是带有条件逻辑的存根。
另一件我认为我已经明白的事情是,模拟对象与依赖注入有关,这个概念昨天我才理解。
我不明白的是我为什么要使用它们。我似乎找不到任何在线上可以正确解释它们的具体例子。
请问有人可以向我解释这些概念吗?
我刚开始阅读的是 专业的C#测试驱动开发:使用TDD开发实际应用程序
我很难理解存根(stubs),伪造对象(fakes)和模拟对象(mocks)。据我目前所了解的,它们都是用于单元测试项目的假对象,而模拟对象则是带有条件逻辑的存根。
另一件我认为我已经明白的事情是,模拟对象与依赖注入有关,这个概念昨天我才理解。
我不明白的是我为什么要使用它们。我似乎找不到任何在线上可以正确解释它们的具体例子。
请问有人可以向我解释这些概念吗?
根据我以前的阅读,以下是我认为每个术语的含义:
Stub
在这里,您将一个方法的结果存根(stub)为已知值,只是为了让代码运行而没有问题。例如,假设您有以下内容:
public int CalculateDiskSize(string networkShareName)
{
// This method does things on a network drive.
}
你并不关心这个方法的返回值,因为它并不重要。此外,如果网络驱动器不可用,执行时可能会导致异常。因此,你需要对结果进行桩处理,以避免出现潜在的执行问题。
所以最终你会做出类似以下的操作:
sut.WhenCalled(() => sut.CalculateDiskSize()).Returns(10);
伪造
使用伪造数据,或创建对象的伪造实例。典型的例子是仓储类。看看这个方法:
public int CalculateTotalSalary(IList<Employee> employees) { }
通常上述方法将传递从数据库中读取的员工集合。但是在单元测试中,您不希望访问数据库。因此,您需要创建一个虚假的员工列表:
IList<Employee> fakeEmployees = new List<Employee>();
你可以向fakeEmployees
添加项目并断言预期结果,例如总工资。
模拟对象
在使用模拟对象时,您打算验证这些模拟对象上的某些行为或数据。例如:
您希望验证在测试运行期间执行了特定方法,以下是使用Moq模拟框架的通用示例:
public void Test()
{
// Arrange.
var mock = new Mock<ISomething>();
mock.Expect(m => m.MethodToCheckIfCalled()).Verifiable();
var sut = new ThingToTest();
// Act.
sut.DoSomething(mock.Object);
// Assert
mock.Verify(m => m.MethodToCheckIfCalled());
}
希望以上内容能稍微澄清一些问题。
编辑:
Roy Osherove是测试驱动开发的著名倡导者,他在这个主题上有一些很好的信息。你可能会发现它非常有用:http://artofunittesting.com/m
是传递到Expect()
函数中的参数。它是一个lambda表达式。它没有在任何地方声明,只是存在于Expect()
lambda内部。var mock = new Mock<ISomething>();
意味着您正在创建一个实现ISomething
的对象,因此ISomething
是一个接口,而不是一个对象。然后,您可以使用mock
来控制方法在调用时如何响应,例如通过Expect()
函数。 - Jason Evans这本 PHP Unit 的手册对我作为入门者帮助很大:
“有时候,由于系统受到其他无法在测试环境中使用的组件的影响,测试系统变得非常困难。这可能是因为它们不可用,它们不能返回所需的测试结果,或者执行它们会产生不良的副作用。在其他情况下,我们的测试策略要求我们更多地控制或查看 SUT 的内部行为。” 更多信息:https://phpunit.de/manual/current/en/test-doubles.html
当我寻找关于“测试替身”(如模拟、伪造、存根等)的更好的“介绍”时,我发现了更多内容。