Reputation: 129
I am writing the test cases for my controller class which is a Spring Boot Application and I want to write the test cases only for controller class which invokes Service and Service to Repository. I am using SpringBootTest which is used for to create the instances for all of my beans. I want to mock only Database calls and external api calls.
MyController {
@Autowired
MyService service;
//Api call for getDetails
service.getDetails();
}
MyService {
@Autowired
MyRepository repo;
}
MyControllertest {
@Autowired
MyController controller;
@Mock
MyRepository repoMock;
@Before
public void setup(){
// intit mocks
}
@Test
public void myTest(){
when(repoMock.getDetails()).thenReturn(null);
controller.getdetails();
}
}
When I run the test case, it is not using the mock Repository, instead of that using the @Autowired
Repository which is mentioned in the Service class.
Can anyone please explain me how to mock the repository from controller class.
Posting so many questions in this blog, but am not getting any responses.
Upvotes: 6
Views: 18545
Reputation: 11
This works fine:
@WebMvcTest(MyController.class)
public class WebMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private MyRepository repository;
@SpyBean
private MyService service;
@InjectMocks
private MyController controller;
@Test
public void saveTest() throws Exception {
String content = "";
long id=Long.MAX_VALUE;
Entity entityToSend = new Entity(id, content);
Gson gson = new Gson();
when(repository.save(any(Entity.class)).thenReturn(Optional.of(entityToSend));
mockMvc.perform(MockMvcRequestBuilders
.post("/api/save")
.content(gson.toJson(entityToSend))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value(Long.MAX_VALUE))
.andExpect(MockMvcResultMatchers.jsonPath("$.content").value(content));
}
Upvotes: 0
Reputation: 2376
This worked for me:
@MockBean
private OrderRepository orderRepository;
@SpyBean
private OrderService orderService;
@InjectMocks
private OrderController orderController;
Upvotes: 2
Reputation: 27068
It is not using your Mocks because you are not injecting those mocks into your controller / service classes. Instead you are Autowiring it.
Correct way to do it is
@RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
@InjectMocks
MyController controller;
.....
}
Even better solution would be to get rid of Field Injection and use Constructor Injection
For example in your Controller and Service class. Instead of using @Autowired on Fields you can do it on Contructor. For example
class MyService {
private final MyRepository repo;
@Autowired
public MyService(final MyRepository repo) {
this.repo = repo;
}
}
Similarly in Controller class
class MyController {
@Autowired
private final MyService service;
public MyController(final MyService service) {
this.service = service
}
}
Doing this way will help you in setting mocks easily during runtime. For example in your test class you could do
@RunWith(MockitoJUnitRunner.class)
public class MyControllertest {
MyController controller;
MyService service;
@Mock
MyRepository repoMock;
@Before
public void setup(){
// init mocks
service = new MyService(repoMock);
controller = new MyController(service);
}
..............
}
Here is a nice article about Field injection
Upvotes: 6
Reputation: 373
3 ways to solve this:
1.One is with with Mockito.
@Mock
MyRepository repoMock;
@InjectMocks
MyService service;
What this does is it matches the MyService class fields by Class type, and assigns the Mocked variables to those fields. So it injects the mocks to your service. This has nothing to do with Spring.
2.Refactor the MyService constructor, so dependencies can be Injected via Constructor. @Autowired works with constructors also, and because of this it's the preferred way.
@Autowired
public MyService(MyRespoitory repository){
this.repository = repository;
}
3.Use Springs test environment:
https://docs.spring.io/spring-security/site/docs/current/reference/html/test-mockmvc.html
And define a bean that is a mocked version of your repository
@Bean
public MyRepository mockMyRepository(){
return mock(MyRespository.class);
}
This can be slow and tiresome for Service testing.
Second one is the most preferred and easiest way.
Upvotes: 0
Reputation: 24540
You must not call
MockitoAnnotations.initMocks(this);
in the setUp
method because the SpringRunner
already initializes the mocks. By calling initMocks
you assign a new object to repoMock
. All Mockito actions like when
are performed on the new object, but Spring still uses the "old" MyRepository
that was created by the SpringRunner
.
Upvotes: 0