LocalDateTime集成测试断言错误:toString与数据库秒数长度不一致
LocalDateTime集成测试断言错误:toString()输出与数据库存储秒数长度不一致
在做LocalDateTime集成测试时碰到断言错误,搞不懂为什么toString()输出的秒数长度和数据库存储的值不一样。具体错误信息如下:
java.lang.AssertionError: 1 expectation failed.
JSON path _embedded.positionsSnapshotDToes.linkTime doesn't match.
Expected: <[2022-11-09T10:01:03.152146400]>
Actual: [2022-11-09T10:01:03.152146]
我用JPA存储实体,对应的POJO字段定义:
@Column(name = "LINK_TIME") private LocalDateTime linkTime;
测试代码如下:
@Test shouldPassLinkTime() { final LocalDateTime anyLinkTime = LocalDateTime.now(); posSnapshotRepo.save( PositionsSnapshot.builder() .linkTime(anyLinkTime) .build() ); SnapshotFilterDTO dto = SnapshotFilterDTO.builder() .build(); given() .spec(correctCredentialsAndPortSpec) .log().ifValidationFails() .contentType("application/json") .body(MAPPER_HELPER.writeValueAsString(dto)) .when() .post("service/unmatched") .then() .statusCode(200) .log().ifValidationFails() .and().body("_embedded.positionsSnapshotDToes.linkTime", equalTo(Arrays.asList(anyLinkTime.toString()))) .and().body("page.totalPages", equalTo(1)) .and().body("page.totalElements", equalTo(1)) .and().body("page.number", equalTo(0)); }
问题根源
- 精度不匹配:
LocalDateTime.now()生成的是纳秒级精度的时间(示例里的.152146400),但多数数据库(比如MySQL的DATETIME类型)只支持微秒级精度(最多6位小数,即.152146)。JPA存数据时会自动截断超出数据库精度的部分。 - 断言逻辑失误:测试里直接用原始的
anyLinkTime.toString()(纳秒级)和数据库返回的截断后的值(微秒级)对比,自然会不一致。
解决办法
方法1:统一时间精度
- 修改实体类字段注解,明确数据库字段的精度:
@Column(name = "LINK_TIME", columnDefinition = "DATETIME(6)") // 6位对应微秒级 private LocalDateTime linkTime;
- 测试时生成微秒级精度的时间,避免多余的纳秒部分:
final LocalDateTime anyLinkTime = LocalDateTime.now().truncatedTo(ChronoUnit.MICROS);
方法2:用格式化工具统一对比
不要直接用toString(),用DateTimeFormatter把时间格式化为统一字符串后再断言:
// 定义统一格式化器 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; // 测试中格式化后对比 String expectedTime = FORMATTER.format(anyLinkTime.truncatedTo(ChronoUnit.MICROS)); // ... .and().body("_embedded.positionsSnapshotDToes.linkTime", equalTo(Arrays.asList(expectedTime)))
方法3:自定义匹配器忽略精度差异
如果不想修改精度,写个自定义Hamcrest匹配器,只对比到微秒级:
.and().body("_embedded.positionsSnapshotDToes.linkTime", everyItem(matchesLocalDateTimeUpToMicros(anyLinkTime)))
自定义匹配器示例:
private Matcher<String> matchesLocalDateTimeUpToMicros(LocalDateTime expected) { return new TypeSafeMatcher<String>() { @Override protected boolean matchesSafely(String actualStr) { LocalDateTime actual = LocalDateTime.parse(actualStr); return expected.truncatedTo(ChronoUnit.MICROS).equals(actual); } @Override public void describeTo(Description description) { description.appendText("时间字符串微秒级匹配 ").appendValue(expected); } }; }
内容的提问来源于stack exchange,提问作者Ahmet Karakaya
相关产品推荐
相关产品推荐

