java - 在进行单元测试时,在不需要时模拟测试对象的依赖关系是一种好习惯吗?
问题描述
例如,让我们看 3 个类:
乙
C
甲(乙,丙)
所以 A 依赖于 B 和 C。如果 B 和 C 已经过单元测试 - 在为 A 编写单元测试时,如果不需要,用 B 和 C 的模拟对象创建测试对象 A 被认为是一种好习惯至?
解决方案
TL;DR:是的,这是一种很好的做法,因为在 A 类中创建对象 B 和 C 是有原因的,我们希望确保 A 类正确处理交互。
在单元测试中,您需要测试单元测试所针对的类。因此,您不希望在 A 类的单元测试中测试 B 类和 C 类的功能。但是,我们可以假设 A 类将对其 B 类和 C 类的实例做一些有意义的事情,否则在类 A 的构造函数。这就是你模拟它们的原因:要么测试这些实例的目的是否已经实现,要么允许你测试类中的条件。换句话说:您的类是否正确处理与其依赖项的交互?
为了让这些概念更清楚一点,我在下面写了一些伪代码。我用 Mockito 编写了这个,但是另一个单元测试框架可以正常工作:
public class A {
private final RepositoryB repositoryB;
private final HelperC helperC;
@Inject
public A(RepositoryB repositoryB, HelperC helperC){
this.repositoryB = repositoryB;
this.helperC = helperC;
}
public List<ObjectD> performATask(boolean doSomeMagic){
List<ObjectD> ds = repositoryB.getAll();
if(doSomeMagic){
helperC.doSomeMagic(ds);
}
return ds;
}
}
public class ATest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private RepositoryB repositoryB;
@Mock
private HelperC helperC;
@InjectMocks
private A a;
@Test
public performATask_CallMethodWithNoMagic_MuggleListIsReturned(){
List<ObjectD> ds = a.performATask(false);
Mockito.verify(repositoryB).getAll();
Mockito.verifyNoInteractions(helperC);
}
@Test
public performATask_CallMethodWithMagic_MagicalListIsReturned(){
Mockito.when(repositoryB.getAll()).thenReturn(createMuggleTestList());
List<ObjectD> ds = a.performATask(true);
Mockito.verify(repositoryB).getAll();
Mockito.verify(helperC).doSomeMagic(anyList());
}
private List<ObjectD> createMuggleTestList(){
// Create it!
}
}
您在这里看到的是,A 类确实依赖于 B 类(RepositoryB)和 C(HelperC)。在 A 类中,您可以看到 RepositoryB 类用于从数据库中检索数据。数据以列表形式返回。根据传递的布尔参数,HelperC 将或不会对检索到的数据执行一些魔术。我们不在乎它有什么魔力,只关心它是基于布尔参数发生的。同样,我们不关心 RepositoryB 如何从数据库中检索其数据。这就是那些各自类的单元测试所担心的。我们想知道的是:A 类是否仍然调用 RepositoryB.getAll 并且是否调用 helperC.doSomeMagic?
如果调用 performATask(false),则第一个单元测试测试是否只调用了 RepositoryB.getAll(并且完全忽略了 HelperC)。第二个单元测试测试是否调用了 RepositoryB.getAll 以及如果调用了 performATask(true) 则测试了 HelperC.doSomeMagic。另请注意,RepositoryB 模拟用于使用 Mockito.when() 返回测试列表。这在第二个单元测试中很重要:
调用模拟方法实际上不会做任何事情;毕竟,这是一个模拟。但是如果在第二个单元测试中调用 HelperC.doSomeMagic,则 HelperC 上的验证需要一个 List。这是有道理的,因为您要确保始终使用 List 而不是其他东西(例如 null)调用 HelperC.doSomeMagic,因为这是 A 类的理想行为。但是 RepositoryB.getAll() 将 - 如果 RepositoryB 是一个模拟 -不返回任何内容,因此将返回 null。然后,此验证将失败(因此您的单元测试将失败,这是理所当然的):
Mockito.verify(helperC).doSomeMagic(anyList());
它希望将一个列表传递给 doSomeMagic,而不是 null。
所以mock也用于在特定条件下返回一个List。(在这种情况下, Mockito.when 用于返回在单元测试中创建的测试列表。)
推荐阅读
- java - 为什么if else语句在这个程序中不起作用。请需要一些解释,虽然我无法继续学习
- r - 如何保存 ShinyFiles 目录输入的基本名称以传递给函数
- amdp - AMDP 选择查询正在获取错误的记录
- c# - 计算 CRC64 以匹配报告中给出的 Azure 值
- python - Python 魔术函数 vs %%writefile
- node.js - 未捕获的错误:连接失败。rtcpeerconnection.t._pc.onconnectionstatechange
- python - 如何确定使用 python 和 jinja 运行的 logstash 实例
- node.js - 在 mongoose 上使用 MongoDB Node.js 驱动程序的优缺点,反之亦然
- webpack - Tailwind with Sass:无效的 PostCSS 插件位于:plugins[0]
- javascript - 导出 React 组件时对象引用“未定义”