injectmocks
上周,我写了有关初始化Mockito模拟和我个人喜好的方法。 我仍在从事旧项目,我想更深入地了解所使用的Mockito的某些功能。
例如,Mockito的开发人员在设计上采取了强烈的自决立场:Mockito只能模拟公共非最终实例方法。 我完全赞同这一点。 要超出此范围,您必须使用PowerMock (我之前写过 )。 很好,因为对我而言,在类路径上发现PowerMock是代码气味的确定标志。 您正在使用的库需要进行一些设计上的改进...或者绝对可以编写代码。
但是,我认为Mockito在其API中有些类似于PowerMock的危险功能。 这样的功能之一就是能够通过反射注入依赖项的依赖项。 不清楚吗 让我们来看一个具有以下类层次结构的示例:
![示例类图](https://i-blog.csdnimg.cn/blog_migrate/a650bb8fd040c9be1d1b26868f1dca20.png)
单元测试的规则要求在测试ToTest
,我们将模拟依赖项DepA
和DepB
。 让我们进一步扩展示例, DepA
和DepB
是以下类:
- 由于它们来自第三方库/框架,因此超出了我们的能力范围
- 以难以模拟的方式进行设计, 即它们需要大量的模拟行为
在这种情况下,我们将不会仅对我们的类进行单元测试,而是对ToTest
, DepA
和DepB
的行为进行集成测试。 由于上述局限性,这不是圣杯,而是可以接受的。
现在让我们DepA
一件事: DepA
和DepB
本身依赖于其他类。 而且由于设计@Autowiring
,它们依赖于通过@Autowiring
进行字段注入-没有构造函数甚至setter注入都可用。 在这种情况下,必须通过Java API或诸如Spring的ReflectionTestUtils
类的实用程序类,使用反射来设置那些依赖项。 在这两种情况下,这都是非常脆弱的,因为它基于属性的名称:
DepAdepA=newDepA();
DepXdepX=newDepX();
DepYdepY=newDepY();
ReflectionTestUtils.setField(depA,"depX",depX);
ReflectionTestUtils.setField(depA,"depY",depY);
提供的Mockito一个简单的替代这个方法:使用@InjectMocks
,是的Mockito能够自动注入嘲笑依赖是在上下文中 。
@RunWith(MockitoJUnitRunner.class)
publicclassTest{
@InjectMocksprivateDepAdepA=newDepA();
@MockprivateDepXdepX;
@MockprivateDepYdepY;
// tests follow
}
由于depX
和depY
都嘲笑我的Mockito,他们是在上下文中,因此可以自动被注入depA
通过的Mockito。 而且由于它们是模拟的,因此可以针对行为进行定位。
虽然有一些缺点。 最重要的是您松开了显式注入-也是我不使用自动装配的原因。 在这种情况下,您的IDE可能会将depX
和depY
报告为未使用。 更糟糕的是, DepA
初始结构的DepA
不会触发未使用字段的任何警告。 最后,作为反思,这些更改可能会导致运行时异常。
最重要的问题@InjectMocks
,但是,它的使用非常方便,太容易了...... @InjectMocks
皮这两个领域注入和太多的依赖问题。 那些应该会受伤,但使用@InjectMocks
时不再@InjectMocks
。 因此,如果将其应用于来自库的依赖项(如depA
和depB
,则别无选择。 但是,如果您开始将其用于自己的类(也就是 ToTest
,那么这肯定看起来像是代码的味道。
injectmocks