Reputation: 5694
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
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
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
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