如何在 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上下文,因此将为BC创建模拟,将它们设置为A,但它会忽略@987654327 @ 注释所以Ad 属性将为空。你需要一个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 注释的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JUnit5 中使用 Mockito

如何在Gradle中使用JUnit 5?

在eclipse中如何使用Junit??

如何将JUnit 4测试添加到JUnit 3测试套件中

如何使用eclipse进行junit测试

如何使用 Junit 在 servlet 中测试业务逻辑?