使用@WebFluxTest测试Post子资源/posts/1/comments失败问题排查
我一眼就看出问题出在你的PostId值对象上——它没有正确实现equals()和hashCode()方法,导致Mockito的参数匹配失效了,最终测试返回空数组才触发了断言错误。
为什么会出现这个问题?
在你的getCommentsByPostId_shouldBeOk测试里,你预设了:
given(comments.findByPost(new PostId("1"))) .willReturn(Flux.just(...));
但实际运行时,PostController的代码会创建一个全新的PostId实例:
return this.comments.findByPost(new PostId(id)); // 这里的id是"1",但这是另一个对象实例
如果PostId没有重写equals()和hashCode(),Java会用默认的对象引用比较逻辑——哪怕两个PostId("1")的内容完全一致,只要是不同的实例,Mockito就会认为这是两个不匹配的参数,不会触发你预设的返回值,而是返回默认的Flux.empty(),接口自然就返回空数组了,断言$[0].id当然找不到值。
快速验证问题的方法
你可以临时修改测试里的given语句,用参数匹配器any(PostId.class)代替具体的实例:
given(comments.findByPost(any(PostId.class))) .willReturn(Flux.just(Comment.builder().id("comment-id-1").content("comment of my first post").build()));
如果修改后测试通过,就坐实了是PostId的equals/hashCode问题。
最终解决方案:给PostId实现正确的equals()和hashCode()
值对象的核心特性是内容相等即对象相等,所以你需要基于id字段来实现这两个方法:
import java.util.Objects; public class PostId { private final String id; public PostId(String id) { this.id = id; // 可选:添加非空校验,避免null值引发后续问题 Objects.requireNonNull(id, "Post id must not be null"); } public String getId() { return id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PostId postId = (PostId) o; return Objects.equals(id, postId.id); } @Override public int hashCode() { return Objects.hash(id); } }
实现后,Mockito就能正确识别内容相同的PostId实例是相等的,你的预设返回值会正常生效,测试就能通过了。
额外的小建议
在createCommentOfPost_shouldBeOk测试里,你预设的是comments.save(comment),但实际代码里保存的是一个全新创建的Comment实例(和测试里的comment不是同一个对象),这里也建议改用参数匹配器:
given(comments.save(any(Comment.class))) .willReturn(Mono.just(comment));
这样可以避免因为Comment的equals/hashCode问题导致的测试失败,让测试逻辑更健壮。
内容的提问来源于stack exchange,提问作者Hantsy

