SpringBoot Controller测试Mock返回null问题根因咨询
SpringBoot Controller单元测试Mock返回null根因解析
你的猜测完全正确,问题核心出在Mockito的参数匹配逻辑和Java类默认的equals比对规则上,具体拆解如下:
核心逻辑说明
- 你在Controller层单元测试中注入的
mMakerService是Mockito生成的Mock代理对象,不是真实的Service实例:所有对该对象的方法调用,都需要先匹配你写的打桩规则,匹配成功才会返回你预设的返回值;匹配失败直接返回引用类型的默认值null,完全不会执行真实Service中的业务逻辑,这就是你调试时无法进入Service层断点的直接原因。 - Mockito默认的参数匹配规则是:调用方法时传入的实际参数,和你打桩时传入的参数,通过
equals()方法比对返回true,才算匹配成功。
未加@EqualsAndHashCode时匹配失败的原因
- 没有在
CreateMonsterDto.Request类上加@EqualsAndHashCode注解时,该类没有重写equals()和hashCode()方法,会直接使用Object类的默认实现:只有两个对象指向同一块内存地址(即引用完全相同)时,equals才会返回true,本质比对的是对象内存地址,不是属性内容。 - 你的测试代码里两处
createMonster方法调用传入的参数其实是不同的实例:- 打桩后手动调用的那一次,传入的是你通过
getCreateRequest()方法新生成的Request对象 - MockMvc发起HTTP请求时,Spring会先把请求体里的JSON反序列化成一个全新的Request对象,再传给Controller层注入的mock service
这两个Request对象哪怕所有属性值完全一致,也是内存地址不同的两个独立实例,默认equals比对返回false,Mockito识别不到匹配的打桩规则,自然返回null。
- 打桩后手动调用的那一次,传入的是你通过
添加@EqualsAndHashCode后修复的原因
Lombok的@EqualsAndHashCode(of = "id")注解会为类自动重写equals()和hashCode()方法,只要两个Request实例的id属性值相等,equals比对就返回true。这时候不管是测试代码里builder生成的实例,还是Spring反序列化JSON生成的实例,只要id一致就会被Mockito判定为参数匹配,打桩规则正常生效,就会返回你预设的返回值,不会再出现null。
测试代码存在的额外问题
- 你当前MockMvc发起请求时,传入的请求体是
CreateMonsterDto.Response对象序列化的结果,正常创建接口应该接收Request类型的参数,现在能跑通纯粹是因为两个类字段名一致,反序列化生成的Request对象id刚好匹配打桩规则,属于巧合,建议改成传入Request对象的序列化结果。 - 测试代码中手动调用
mMakerService.createMonster(getCreateRequest())属于多余代码,单元测试只需要校验MockMvc触发的接口调用逻辑即可,不需要手动调用Service方法,这也是你后面需要校验times(2)调用次数的原因,去掉手动调用后改成校验times(1)更符合测试逻辑。
内容的提问来源于stack exchange,提问作者doosic C
相关产品推荐
相关产品推荐

