Mockito 模拟对象被忽略并调用实际函数

Mockito Mock object being ignored and actual function being called

模拟对象似乎不起作用,正在调用实际函数。

我的控制器Class如下:-

@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());
    }

}

我的ClassB如下:-

@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;
    }
}

这是我的测试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));

    }
}

如果你注意到我在 Class B 中创建了 class C 的静态变量。这是程序结构本身的一部分,无法更改。

是否可以根据此代码模拟 client.search 函数?

解决问题的一种快速方法是直接模拟 B 和存根 B.getAns

为了模拟静态 B,您应该将 mockito-inline 依赖项添加到 pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>3.8.0</version>
    <scope>test</scope>
</dependency>

您可以这样使用Mockito.mockStatic

try (MockedStatic<B> mockedB = Mockito.mockStatic(B.class)) {
    mockedB.when(() -> B.getAns(anyString())).thenReturn(obj);
    // ...
}

因此,您的测试代码将是:

@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));
    }
}

请注意,当您使用 MockedStatic 时,您最好使用 try-with-resources 模式并像上面一样在此范围内进行测试。

阅读更多关于 mockStatic 的信息:https://www.baeldung.com/mockito-mock-static-methods

通过 运行 在调试模式下的测试,我能够找出问题所在。

我发现我的 Class B 中的 @PostConstruct 函数在我的测试函数之前被调用。所以 class B 正在创建自己的 beanClient 对象,这与我的测试 class 中的模拟不同。这就是为什么它进入函数而不是模拟它的原因。

我能够通过像这样更改 Class B 来解决它:-

@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;
 }

我不得不将其更改为 non-static 函数。