Spring Boot:测试包含 Java 时间的 ResponseEntity

Posted

技术标签:

【中文标题】Spring Boot:测试包含 Java 时间的 ResponseEntity【英文标题】:Spring Boot: Testing ResponseEntity Containing Java Time 【发布时间】:2022-01-13 14:29:42 【问题描述】:

我需要测试一个包含 java.time.Instant 字段的 Rest API 响应正文。不幸的是,由于实际时间戳和预期时间戳之间存在毫秒差异,因此无法通过测试。我只是想知道如何模拟系统时钟或以某种方式配置测试上下文以使测试通过:

响应正文类:

public class ApiError 
    private HttpStatus httpStatus;
    private Instant timestamp;
    private List<ErrorDetail> errorDetails;

    public ApiError(HttpStatus httpStatus) 
        this.httpStatus = httpStatus;
        this.timestamp = Instant.now();
    

// the rest of class is omitted for brevity

测试类:

@WebMvcTest(controllers = UserController.class)
class UserControllerTest 

    @Autowired MockMvc mockMvc;
    @Autowired ObjectMapper mapper;
    ApiError apiError;

    @Test
    public void givenBlankField_WhenRequestIsReceived_ThenApiErrorGenerated() throws Exception 
        SignUpRequest request = new SignUpRequest()
                .setFirstName(" ")  //validation error occurs
                .setLastName("Doe")
                .setPassword("123")
                .setConfirmPassword("123")
                .setEmail("john.doe@something.com")
                .setConfirmEmail("john.doe@something.com");

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/api/users/signup")
                .content(mapper.writeValueAsString(request))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isBadRequest()).andReturn();

        String actualResponseBody = result.getResponse().getContentAsString();

        // Excpected response body:
        apiError = new ApiError(HttpStatus.BAD_REQUEST);
        FieldValidationErrorDetail errorDetail = new FieldValidationErrorDetail.Detail()
                .field("firstName").message("NotBlank.firstName").rejectedValue(" ").build();
        apiError.setErrorDetails(List.of(errorDetail));
        String expectedResponseBody = mapper.writeValueAsString(apiError);

        assertEquals(expectedResponseBody, actualResponseBody); //fails due to milliseconds difference between actual timestamp and expected timestamp
    

【问题讨论】:

【参考方案1】:

说明

在这里,您在两个不同的时间实例化 timestamp 字段并进行比较。他们将永远无法匹配。

解决方案

我建议避免在您的测试中实例化new ApiError(HttpStatus.BAD_REQUEST),并简单地单独验证每个字段。对于timestamp 字段,只需验证它是否设置为非空值,或者如果您想变得迂腐,请验证它在最后 X 秒内有一个值,而不是您当前的精确毫秒验证检查。

替代解决方案(Hacky Workaround)

如果您想根据 JSON 字符串继续验证 JSON 字符串,只需将响应正文转换为对象并将其时间戳窃取到您的比较对象中。

【讨论】:

感谢您的回复!我实际上正在寻找一种模拟系统时钟的方法。我在互联网上找到了一些覆盖时钟的技术,但无法成功地将它们应用于我的测试用例。您还有其他想法吗? 你可能会比模拟系统时钟更麻烦。我认为这样做没有任何附加价值。如果您希望将字段设置为相同的值,您始终可以将新 ApiError() 的字段设置为与收到的字段相同的值,但同样,我不确定这会完成什么。

以上是关于Spring Boot:测试包含 Java 时间的 ResponseEntity的主要内容,如果未能解决你的问题,请参考以下文章

Java Spring Boot 测试:如何从测试上下文中排除 java 配置类

Spring boot maven插件:包含测试资源

使用 Spring Boot 在单元测试中配置集成测试或使用 Spring JDBC?

如何 JUnit 测试 Spring-Boot 的 Application.java

Spring Boot的常见配置项解析

Java - Spring boot - 集成测试 - TestEntityManager 没有被注入