如何在Junit中使用@InjectMocks和@Autowired注释

USe*_*299 36 java junit spring mocking mockito

我有一个A类,它使用3个不同的类和自动装配

public class A () {

    @Autowired
    private B b;

    @Autowired
    private C c;

    @Autowired
    private D d;
}
Run Code Online (Sandbox Code Playgroud)

在测试它们时,我希望只有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;
}
Run Code Online (Sandbox Code Playgroud)

甚至可以这样做吗?

Saj*_*ran 37

应该是这样的

@RunWith(SpringJUnit4ClassRunner.class)
public class aTest () {

    @Mock
    private B b;

    @Mock
    private C c;

    @Autowired
    @InjectMocks
    private A a;

}
Run Code Online (Sandbox Code Playgroud)

如果你想D成为Autowired不需要你做任何事情Test类.你Autowired A应该有正确的实例D.此外,我认为你需要使用SpringJUnit4ClassRunnerAutowiring工作,用contextConfiguration正确的设定.因为你没有使用MockitoJunitRunner你需要初始化mocks自己使用

MockitoAnnotations.initMocks(java.lang.Object testClass)

  • 重要的是要注意,对象将由自动装配创建,并且模拟器将由注册器注入.这从来没有发生在我身上,因为我的对象只有一个构造函数,并且没有这些字段的setter,所以模拟的实例没有被注入(并且没有报告它没有注入模拟).这打破了我希望的模型(不可变对象),因此,像其他人一样,我相信InjectMocks可能是一个坏主意. (16认同)
  • 有一种情况,这种方法不起作用:当`A`用`@Transactional`注释时(或者用@@ Transactional`注释的方法); 请参阅http://stackoverflow.com/questions/12857981/transactional-annotation-avoids-services-being-mocked和http://stackoverflow.com/questions/21124326/how-to-inject-mock-into-service-that都具有一个事务 (2认同)
  • 一个重要的问题:如果你的类的构造函数中有@Autowired,并且你尝试模拟的字段设置为final,那么注入将不起作用。 (2认同)

ant*_*ar9 15

除了接受的答案之外,如果您使用 spring-boot,使用@MockBean注释会更容易(创建一个模拟并将其作为 bean 添加到上下文中,如果存在则替换它):

@RunWith(SpringRunner.class)
public class aTest () {

    @MockBean
    private B b;

    @MockBean
    private C c;

    @Autowired
    private A a;
}
Run Code Online (Sandbox Code Playgroud)

如果您不使用 spring-boot,@Autowired + @InjectMocks 的问题是 Spring 将首先加载 bean B 和 C 不需要的实例,然后将它们替换为模拟。这是一种浪费,并且可能具有您不想要/无法加载的传递依赖项。始终建议为测试加载最小的 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;
}
Run Code Online (Sandbox Code Playgroud)


Tho*_*mas 6

我遇到了同样的问题,并尝试了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);
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 这是更好的解决方案,因为您可以使用 D 类,即使它不是 A 类内部的 @Autowired! (2认同)