为什么模拟测试框架有帮助?
Why are mocking test frameworks helpful?
似乎我看过的所有 Mockito 示例,"fake" 他们正在测试的对象的行为。
如果我有一个具有方法的对象:
public int add(int a, int b) {return a+b}
我会简单地使用 JUnit 断言传入的两个整数是否会产生正确的输出。
对于我在 Mockito 中看到的所有示例,人们正在做 when.Object.add(2,3).thenReturn(5)
之类的事情。如果您所做的只是告诉对象如何在测试端而不是对象端执行操作,那么使用此测试框架有什么意义?
模拟框架非常适合通过模拟系统的依赖项来测试系统;如果您正在测试 add
,则不会使用模拟框架来模拟或存根 add
。让我们进一步分解一下:
测试add
模拟框架不适合测试上面的add
方法。除了非常稳定且经过严格测试的 JVM 和 JRE 之外,没有其他依赖项。
public int add(int a, int b) {return a+b}
但是,如果它像这样与另一个对象交互:
,那么测试您的添加方法可能会有好处
public int add(int a, int b, AdditionLogger additionLogger) {
int total = a + b;
additionLogger.log(a, b, total);
return total;
}
如果 AdditionLogger 尚未编写,或者如果它是为了与真实服务器或其他外部进程通信而编写的,那么模拟框架绝对有用:它会帮助您提出 AdditionLogger 的虚假实现,因此您可以测试您的真实方法与它的交互。
@Test public void yourTest() {
assertEquals(5, yourObject.add(2, 3, mockAdditionLogger));
verify(mockAdditionLogger).log(2, 3, 5);
}
测试add
的消费者
巧合的是,模拟框架也不太可能适合测试上述方法的使用者。毕竟,调用 add
并没有什么特别危险的,所以假设它存在,您可能可以在外部测试中调用真实的。 2 + 3 将始终等于 5,并且您的计算没有副作用,因此通过模拟或验证几乎没有什么收获。
但是,让我们为您的对象提供另一种方法,将两个数字与一点点随机噪声相加:
public int addWithNoise(int a, int b) {
int offset = new Random().nextInt(11) - 5; // range: [-5, 5]
int total = a + b + offset;
return total;
}
有了这个,您可能很难针对此方法编写稳健的 assert
式测试;毕竟,结果会有些随机!相反,为了使 assert
式测试更容易,也许我们可以去掉 addWithNoise
以使其中一些更可预测。
@Test public void yourTest() {
when(yourObjectMock.addWithNoise(2, 3)).thenReturn(6);
// You're not asserting/verifying the action you stub, you're making the dependency
// *fast and reliable* so you can check the logic of *the real method you're testing*.
assertEquals(600, systemUnderTestThatConsumesYourObject.doThing(yourObjectMock));
}
总结
在与 add
等知名操作或 List
等知名接口进行交互时,可以更轻松地解释模拟和模拟语法,但这些示例通常不是模拟的真实情况是需要的。请记住,当您不能使用真实的
. 时,模拟仅对 模拟被测系统周围的依赖关系真正有用。
单元测试的目标是在不连接到任何外部系统的情况下测试功能。如果您要连接到任何外部系统,则视为集成测试。
在执行单元测试时,系统可能需要一些数据,这些数据可能在 Systems/Integration 测试期间从外部系统检索为数据库、web/rest 服务、API 等。在这种情况下,我们需要提供 mock/fake 数据来测试某些业务规则或任何其他形式的逻辑。
如上所述,单元测试可确保特定代码单元与给定的 fake/mocked 数据集一起工作,并且在集成环境中应该以类似的方式运行。
似乎我看过的所有 Mockito 示例,"fake" 他们正在测试的对象的行为。
如果我有一个具有方法的对象:
public int add(int a, int b) {return a+b}
我会简单地使用 JUnit 断言传入的两个整数是否会产生正确的输出。
对于我在 Mockito 中看到的所有示例,人们正在做 when.Object.add(2,3).thenReturn(5)
之类的事情。如果您所做的只是告诉对象如何在测试端而不是对象端执行操作,那么使用此测试框架有什么意义?
模拟框架非常适合通过模拟系统的依赖项来测试系统;如果您正在测试 add
,则不会使用模拟框架来模拟或存根 add
。让我们进一步分解一下:
测试add
模拟框架不适合测试上面的add
方法。除了非常稳定且经过严格测试的 JVM 和 JRE 之外,没有其他依赖项。
public int add(int a, int b) {return a+b}
但是,如果它像这样与另一个对象交互:
,那么测试您的添加方法可能会有好处public int add(int a, int b, AdditionLogger additionLogger) {
int total = a + b;
additionLogger.log(a, b, total);
return total;
}
如果 AdditionLogger 尚未编写,或者如果它是为了与真实服务器或其他外部进程通信而编写的,那么模拟框架绝对有用:它会帮助您提出 AdditionLogger 的虚假实现,因此您可以测试您的真实方法与它的交互。
@Test public void yourTest() {
assertEquals(5, yourObject.add(2, 3, mockAdditionLogger));
verify(mockAdditionLogger).log(2, 3, 5);
}
测试add
的消费者
巧合的是,模拟框架也不太可能适合测试上述方法的使用者。毕竟,调用 add
并没有什么特别危险的,所以假设它存在,您可能可以在外部测试中调用真实的。 2 + 3 将始终等于 5,并且您的计算没有副作用,因此通过模拟或验证几乎没有什么收获。
但是,让我们为您的对象提供另一种方法,将两个数字与一点点随机噪声相加:
public int addWithNoise(int a, int b) {
int offset = new Random().nextInt(11) - 5; // range: [-5, 5]
int total = a + b + offset;
return total;
}
有了这个,您可能很难针对此方法编写稳健的 assert
式测试;毕竟,结果会有些随机!相反,为了使 assert
式测试更容易,也许我们可以去掉 addWithNoise
以使其中一些更可预测。
@Test public void yourTest() {
when(yourObjectMock.addWithNoise(2, 3)).thenReturn(6);
// You're not asserting/verifying the action you stub, you're making the dependency
// *fast and reliable* so you can check the logic of *the real method you're testing*.
assertEquals(600, systemUnderTestThatConsumesYourObject.doThing(yourObjectMock));
}
总结
在与 add
等知名操作或 List
等知名接口进行交互时,可以更轻松地解释模拟和模拟语法,但这些示例通常不是模拟的真实情况是需要的。请记住,当您不能使用真实的
单元测试的目标是在不连接到任何外部系统的情况下测试功能。如果您要连接到任何外部系统,则视为集成测试。
在执行单元测试时,系统可能需要一些数据,这些数据可能在 Systems/Integration 测试期间从外部系统检索为数据库、web/rest 服务、API 等。在这种情况下,我们需要提供 mock/fake 数据来测试某些业务规则或任何其他形式的逻辑。
如上所述,单元测试可确保特定代码单元与给定的 fake/mocked 数据集一起工作,并且在集成环境中应该以类似的方式运行。