【问题标题】:Match LocalDateTime using Hamcrest in MVC Test在 MVC 测试中使用 Hamcrest 匹配 LocalDateTime
【发布时间】:2020-08-05 16:43:57
【问题描述】:

我正在努力使用 Mockito 和 Hamcrest 为 LocalDateTime 字段实现 Spring MVC 控制器测试。

@WebMvcTest(controllers = FooController.class)
class FooControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private FooService fooService;

  @Test
  void testController() throws Exception {
    Foo foo = new Foo(LocalDateTime.now());
    List<Foo> allFoos = Arrays.asList(foo);

    given(fooService.getAllFoos()).willReturn(allFoos);

    mockMvc.perform(get("/foos").contentType(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$", hasSize(1)))
        .andExpect(jsonPath("$[0].timeOfUpdate", equalTo(foo.getTimeOfUpdate()), LocalDateTime.class));
  }
}

控制器返回一个 JSON 序列化对象 Foo,如下所示:

@Getter
@Setter
@AllArgsConstructor
class Foo {
  private LocalDateTime timeOfUpdate;
}

JSON 响应如下所示:

[
  {
     "timeOfUpdate":"2020-08-05T18:40:30.7416748"
  }
]

但是,当我尝试将字段 timeOfUpdateequalTo 匹配时,我收到以下错误:

java.lang.AssertionError: JSON path "$[0].timeOfUpdate"
Expected: <2020-08-05T18:40:30.741674800>
     but: was "2020-08-05T18:40:30.7416748"

所以这似乎是一个精度错误,但我不知道如何解决这个问题。

更新:

我已将 Matcher 更改为以下内容:

andExpect(jsonPath("$[0].timeOfUpdate").value(foo.getTimeOfUpdate()));

现在它似乎强制类型正确,但断言仍然失败:

java.lang.AssertionError: JSON path "$[0].timeOfUpdate" expected:<2020-08-05T19:19:05.739893500> but was:<2020-08-05T19:19:05.7398935>
Expected :2020-08-05T19:19:05.739893500
Actual   :2020-08-05T19:19:05.7398935

更新 2

这是使测试变为绿色的一种解决方法:

.andExpect(jsonPath("$[0].statusRecord.timeOfUpdate", is(person.getStatusRecord().getTimeOfUpdate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss")))));
@Getter
@Setter
@AllArgsConstructor
class Foo {
  @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'hh:mm:ss")
  private LocalDateTime timeOfUpdate;
}

【问题讨论】:

  • 该消息似乎表示类型错误,而不是精度错误。我这样说是基于尖括号和双引号。您确定 jsonPath() 在比较之前转换为 LocalDateTime 吗?另外,我认为这还不是问题,但是如果您想测试它生成的时间,您应该将Clock 实例注入您的服务。否则,由于实际精度而不是格式,您最终会遇到虚假错误。
  • @erickson 谢谢!你完全正确。我已经编辑了这个问题,并找到了一种强制类型正确的方法。然而 AssertionError 仍然发生,现在似乎很清楚这是由于精度造成的。
  • 如果您设置为使用LocalDateTime,您的解决方法似乎不错。通常,即使接收者可以推断时区,这种格式也不能保留绝对时间,因为秋季夏令时转换。我建议使用Instant 进行API 签名,并序列化为ISO_INSTANTISO_OFFSET_DATE_TIME,这样客户就不必猜测您真正的意思是什么时间。将您在此处所做的精度限制为秒或毫秒以实现最广泛的互操作性也可能会有所帮助。

标签: java spring-mvc junit mockito hamcrest


【解决方案1】:

您只需将toString() 用于.value(foo.getTimeOfUpdate().toString()) jsonPath("$[0].timeOfUpdate" 是 json 字符串。
我也遇到了同样的问题。

这是更改后的测试类。

@WebMvcTest(controllers = FooController.class)
class FooControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private FooService fooService;

  @Test
  void testController() throws Exception {
    Foo foo = new Foo(LocalDateTime.now());
    List<Foo> allFoos = Arrays.asList(foo);

    given(fooService.getAllFoos()).willReturn(allFoos);

    mockMvc.perform(get("/foos").contentType(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$", hasSize(1)))
        .andExpect(jsonPath("$[0].timeOfUpdate").value(foo.getTimeOfUpdate().toString()));
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多