I have a controller class ControllerClass that manages two class services:
ServiceA that parse some files
ServiceB that manage the filesystem
I want to test ControllerClass and in particular:
ServiceA Autowired class
ServiceB Mock this service, with a mock class that implement the intercace returning always the fixed value.
How can I do?
Starting from a simple spring-web application with the following test:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MyControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void testGreeting() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("A response: I am real ServiceA!, B response: I am real ServiceB!")));
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MyControllerMockBeanTest {
@MockBean
private ServiceB mockB;
@Before
public void setup() {
Mockito.when(mockB.greeting()).thenReturn("I am mock Service B!");
}
@Autowired
private MockMvc mvc;
@Test
public void testGreetingMock() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("A response: I am real ServiceA!, B response: I am mock Service B!")));
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
// activate "test" profile
@ActiveProfiles("test")
// set custom config classes (don't forget Application)
@ContextConfiguration(classes = {TestConfig.class, Application.class})
public class MyControllerTest {
// define configuration for "test" profile (inline possible)
@Profile("test")
@Configuration
static class TestConfig {
@Bean
// !
@Primary
// I had an (auto configuration) exception/clash,
// when using *same bean name*, so *not* 'serviceB()', plz.
public ServiceB mockB() {
// prepare...
ServiceB mockService = Mockito.mock(ServiceB.class);
Mockito.when(mockService.greeting()).thenReturn("I am Mock Service B!");
// and return your mock object!
return mockService;
}
}
@Autowired
private MockMvc mvc;
@Test
public void testGreetingMock() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("A response: I am real ServiceA!, B response: I am Mock Service B!")));
}
}
Complete sample at github.
And I am quite sure, that the list of solutions is not complete.
@MockBean looks like good candidate for your use-case.
This annotations behaves in following way:
After you wired mock bean in your test class, you can stub it, to always return some value. For example, for specific test just add something like this to your test:
@Autowired
private ServiceA serviceA;
@MockBean
private ServiceB serviceB;
@Test
public void testSomething() {
when(serviceB.doSomething()).thenReturn("fixed response");
// ...
}
If you want stub for all tests - put the stubbing in setup method:
@Autowired
private ServiceA serviceA;
@MockBean
private ServiceB serviceB;
@Before
public void setup() {
when(serviceB.doSomething()).thenReturn("fixed response");
}
By the way, Spring also provies @SpyBean with similar behaviour as @MockBean
.
Basically there is no difference between spy and mock, if you stub method calls on them. The difference become apperent, when method calls are not stubbed:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With