The mock object does not seem to work and the actual function is being called.
My Controller Class is as follows:-
@RestController
public class A {
@PostMapping(path = "/api/methods", consumes = "application/json", produces = "application/json")
public static ResponseEntity<Object> controllerFunction(@Valid @RequestBody String request,
@RequestHeader(value = "header-content") String header_content) {
JSONObject response = B.getAns(request);
return ResponseEntity.status(HttpStatus.OK).body(response.toString());
}
}
My Class B is as follows:-
@Service
public class B {
private static C client;
@Autowired
private C beanClient;
@PostConstruct
public void init() {
B.client = beanClient;
}
public static JSONObject getAns(String request) {
// This is the line that I intend to mock but gets ignored. It goes into the function search instead.
JSONObject resp = client.search(searchRequest, requestHeaderOptions); // assume that these two variables passed as arguments are not null and have some content.
// searchRequest is of type SearchRequest
// requestHeaderOptions is of type RequestOptions
return resp;
}
}
This is my test class :
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
ControllerApplication.class, A.class, B.class, C.class
})
@ActiveProfiles("test")
public class ProjectTest {
@Mock
private C client;
@InjectMocks
A a;
private MockMvc mockMvc;
@BeforeSuite
public void setup() {
// I have tried both MockitoAnnotations.initMocks and openMocks. Both don't work
MockitoAnnotations.openMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(a).build();
}
@Test(enabled = true)
public void testing() throws Exception {
JSONObject obj = new JSONObject() // assume this object is not null
// This statement doesn't seem to work
doReturn(obj).when(client).search(any(SearchRequest.Class), any(RequestOptions.Class));
MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/api/methods")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("header-content", "something")
.content("someData");
mockMvc.perform(mockRequest)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(jsonResponse));
}
}
If you notice I have created a static variable of the class C in my Class B. This is part of the program structure itself and cannot be changed.
Is it possible to mock the client.search
function given this code?
CodePudding user response:
A quick way to resolve your issue is to mock B
and stub B.getAns
directly.
In order to mock static B, you should add mockito-inline
dependency to your pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
You can use Mockito.mockStatic
in this way:
try (MockedStatic<B> mockedB = Mockito.mockStatic(B.class)) {
mockedB.when(() -> B.getAns(anyString())).thenReturn(obj);
// ...
}
Therefore, your test code will be:
@Test(enabled = true)
public void testing() throws Exception {
try (MockedStatic<B> mockedB = Mockito.mockStatic(B.class)) {
JSONObject obj = new JSONObject() // assume this object is not null
mockedB.when(() -> B.getAns(anyString())).thenReturn(obj);
MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/api/methods")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("header-content", "something")
.content("someData");
mockMvc.perform(mockRequest)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(jsonResponse));
}
}
Note that, when you use MockedStatic
, you had better use try-with-resources
pattern and do your test in this scope like above.
Read more about mockStatic: https://www.baeldung.com/mockito-mock-static-methods
CodePudding user response:
I was able to figure out what the problem was by running the test in debug mode.
I found that the @PostConstruct
function in my Class B was getting called before my test function. So class B was creating its own beanClient
object different from the mock in my test class. That's why it was going into the function and not mocking it.
I was able to resolve it by changing Class B like so:-
@Service
public class B{
@Autowired
private C client;
public JSONObject getAns(String request){
// This is the line that I intend to mock but gets ignored. It goes into the function search instead.
JSONObject resp =client.search(searchRequest,requestHeaderOptions); // assume that these two variables passed as arguments are not null and have some content.
// searchRequest is of type SearchRequest
// requestHeaderOptions is of type RequestOptions
return resp;
}
I had to change it into a non-static function.