USer22999299
USer22999299

Reputation: 5694

How to use @InjectMocks along with @Autowired annotation in Junit

I have a class A which is using 3 differnt classes with autowiring

public class A () {

    @Autowired
    private B b;

    @Autowired
    private C c;

    @Autowired
    private D d;
}

While testing them, i would like to have only 2 of the classes (B & C) as mocks and have class D to be Autowired as normal running, this code is not working for me:

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

    @InjectMocks
    private A a;

    @Mock
    private B b;

    @Mock
    private C c;

    @Autowired
    private D d;
}

Is it even possible to do so?

Upvotes: 76

Views: 85609

Answers (3)

Aníbal
Aníbal

Reputation: 935

In addition to accepted answer, if you are using spring-boot, it's easier to use @MockBean annotation (that creates a mock and add it as a bean to the context, replacing it if it exists):

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

    @MockBean
    private B b;

    @MockBean
    private C c;

    @Autowired
    private A a;
}

In case you are not using spring-boot, the problem with @Autowired + @InjectMocks is that Spring will load unneeded instances for beans B and C first, and then they are replaced by the mocks. This is a waste and could have transitive dependencies that you don't want/can't load. It's always recommended to load the minimum Spring context for your testing. I would recommend this:

@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;
}

Upvotes: 20

Thomas
Thomas

Reputation: 4330

I was facing same problem and tried the answer by Sajan Chandran. It didn't work in my case because I'm using @SpringBootTest annotation to load only a subset of all my beans. The goal is not to load the beans that I'm mocking since they have lot of other dependencies and configurations.

And I found the following variant of the solution to work for me, which is usable in normal case also.

@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);
    }

}

Upvotes: 23

Sajan Chandran
Sajan Chandran

Reputation: 11487

It should be something like

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

    @Mock
    private B b;

    @Mock
    private C c;

    @Autowired
    @InjectMocks
    private A a;

}

If you want D to be Autowired dont need to do anything in your Test class. Your Autowired A should have correct instance of D. Also i think you need to use SpringJUnit4ClassRunner for Autowiring to work, with contextConfiguration set correctly. Because you are not using MockitoJunitRunner you need to initialize your mocks yourself using

MockitoAnnotations.initMocks(java.lang.Object testClass)

Upvotes: 68

Related Questions