如何让Groovy Spock测试用equals而非==比较调用参数
Spring Groovy测试Mock参数匹配失败问题
我为Spring应用编写了如下Groovy测试:
given: def mapProperties = new JSONObject().put( "eligibility", "true").put( "group", "group1") when: trackingService.sendUnifiedOnboardingAssignmentUserUpdate() then: 1 * trackingCollector.send(new UserUpdate(mapProperties))
Tracking Collector是外部组件,在我继承的Specification中定义为Spring Bean:
@SpringBean TrackingCollector trackingCollector = Mock()
trackingService位于领域层,已通过Autowired注入。
测试运行失败,报错信息如下:
Too few invocations for: 1 * trackingCollector.send(new UserUpdate(mapProperties)) (0 invocations) Unmatched invocations (ordered by similarity): 1 * trackingCollector.send(UserUpdate(super=Event(properties={"group":"group1","eligibility":"true"}))) One or more arguments(s) didn't match: 0: argument == expected | | | | | UserUpdate(super=Event(properties={"group":"group1","eligibility":"true"})) (com.package.tracking.collector.UserUpdate@4fe5aac9) | false UserUpdate(super=Event(properties={"group":"group1","eligibility":"true"})) (com.package.tracking.collector.UserUpdate@48bb5a69)
两个UserUpdate对象内容完全一致,仅引用地址不同。为什么Groovy不检查对象实际值而比较引用?该如何修改让测试通过?
问题原因
Groovy结合Spock进行Mock参数匹配时,默认依赖对象的equals()方法判断是否相等。如果你的UserUpdate或其父类Event没有正确重写equals()和hashCode(),就会退化为Java原生的引用比较——即只有同一对象实例才会被判定为相等。从报错信息看,两个对象内容一致但引用不同,说明UserUpdate类的equals()逻辑未按内容实现。
解决方案
1. 重写UserUpdate/Event的equals()和hashCode()
这是最规范的长期解决方案,确保对象按内容比较。示例代码(Groovy写法):
class UserUpdate extends Event { // 字段和构造方法 @Override boolean equals(Object o) { if (this.is(o)) return true if (!(o instanceof UserUpdate)) return false UserUpdate that = (UserUpdate) o return properties == that.properties } @Override int hashCode() { properties?.hashCode() ?: 0 } }
重写后,Spock会自动按对象内容匹配参数,原测试代码无需修改即可通过。
2. 使用Spock闭包匹配参数
如果不想修改实体类,直接在测试中用闭包断言参数的属性值:
then: 1 * trackingCollector.send({ it.properties == mapProperties })
这种方式跳过对象的equals()方法,直接检查目标属性是否匹配,简洁高效。
3. 捕获参数后单独验证
适合需要对参数做复杂验证的场景,先捕获调用时的参数,再逐一验证:
given: def mapProperties = new JSONObject().put("eligibility", "true").put("group", "group1") UserUpdate capturedUpdate = null when: trackingService.sendUnifiedOnboardingAssignmentUserUpdate() then: 1 * trackingCollector.send(_) >> { args -> capturedUpdate = args[0] } and: capturedUpdate.properties == mapProperties
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

