如何在 Junit 中使用 @InjectMocks 和 @Autowired 注释
Posted
技术标签:
【中文标题】如何在 Junit 中使用 @InjectMocks 和 @Autowired 注释【英文标题】:How to use @InjectMocks along with @Autowired annotation in Junit 【发布时间】:2016-03-08 04:23:03 【问题描述】:我有一个 A 类,它使用 3 个不同的自动装配类
public class A ()
@Autowired
private B b;
@Autowired
private C c;
@Autowired
private D d;
在测试它们时,我希望只有 2 个类(B 和 C)作为模拟,并让 D 类正常运行时自动装配,此代码对我不起作用:
@RunWith(MockitoJUnitRunner.class)
public class aTest ()
@InjectMocks
private A a;
@Mock
private B b;
@Mock
private C c;
@Autowired
private D d;
真的有可能吗?
【问题讨论】:
问题是MockitoJUnitRunner
不适用于Spring上下文,因此将为B
和C
创建模拟,将它们设置为A
,但它会忽略@987654327 @ 注释所以A
的d
属性将为空。你需要一个SpringRunner
(就像在接受的答案中一样)来注入bean并使用模拟注释。
【参考方案1】:
应该是这样的
@RunWith(SpringJUnit4ClassRunner.class)
public class aTest ()
@Mock
private B b;
@Mock
private C c;
@Autowired
@InjectMocks
private A a;
如果您希望 D
成为 Autowired
,则无需在您的 Test
类中执行任何操作。您的 Autowired
A
应该有正确的 D
实例。
另外我认为您需要使用SpringJUnit4ClassRunner
才能使Autowiring
工作,并正确设置contextConfiguration
。
因为您没有使用MockitoJunitRunner
,所以您需要使用
mocks
MockitoAnnotations.initMocks(java.lang.Object testClass)
【讨论】:
我有什么办法可以继续使用@RunWith(MockitoJUnitRunner.class) 并解决这个问题?上面的代码只是更大的测试类的一部分,我不能改变我们运行junits的方式 是的,你可以,但在这种情况下你不能使用Autowired
,你必须自己手动编写代码来初始化spring上下文加载A
的实例
有一种情况,这种方法不起作用:当A
被@Transactional
注释时(或者有用@Transactional
注释的方法);见***.com/questions/12857981/…和***.com/questions/21124326/…
需要注意的是,对象将由 Autowiring 创建,而 mock 将由 setter 注入。这从来没有发生在我身上,因为我的对象只有一个构造函数,并且没有这些字段的设置器,所以模拟的实例没有被注入(并且没有报告它没有注入模拟)。这打破了我希望的模型(不可变对象),所以和其他人一样,我相信 InjectMocks 可能是个坏主意。
一个重要的问题:如果你的类的构造函数中有@Autowired,并且你试图模拟的字段设置为final,那么注入将不起作用。【参考方案2】:
我遇到了同样的问题,并尝试了 Sajan Chandran 的答案。在我的情况下它不起作用,因为我使用 @SpringBootTest 注释仅加载我所有 bean 的一个子集。目标不是加载我正在模拟的 bean,因为它们有很多其他依赖项和配置。
我发现以下解决方案的变体对我有用,在正常情况下也可以使用。
@RunWith(SpringRunner.class)
@SpringBootTest(classes=...classesRequired...)
public class aTest ()
@Mock
private B b;
@Mock
private C c;
@Autowired
@Spy
private D d;
@InjectMocks
private A a;
@Before
public void init()
MockitoAnnotations.initMocks(this);
【讨论】:
这是更好的解决方案,因为您可以使用 D 类,即使它不是 A 类中的 @Autowired!【参考方案3】:除了接受的答案之外,如果您使用的是 spring-boot,则使用 @MockBean 注释更容易(创建一个模拟并将其作为 bean 添加到上下文中,如果它替换它存在):
@RunWith(SpringRunner.class)
public class aTest ()
@MockBean
private B b;
@MockBean
private C c;
@Autowired
private A a;
如果你没有使用 spring-boot,@Autowired + @InjectMocks 的问题是 Spring 会首先为 bean B 和 C 加载不需要的实例,然后它们被 mocks 替换。这是一种浪费,并且可能具有您不想要/无法加载的传递依赖项。始终建议为您的测试加载最小的 Spring 上下文。我会推荐这个:
@RunWith(SpringRunner.class)
@Import(A.class, D.class)
@ContextConfiguration(classes = aTest.class)
public class aTest ()
@Bean
private B b()
return Mockito.mock(B.class);
@Bean
private C c()
return Mockito.mock(C.class);
@Autowired
private A a;
【讨论】:
以上是关于如何在 Junit 中使用 @InjectMocks 和 @Autowired 注释的主要内容,如果未能解决你的问题,请参考以下文章