Home > Net >  Mockito Mock object being ignored and actual function being called
Mockito Mock object being ignored and actual function being called

Time:05-21

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.

  • Related