Spock Framework Java测试中Mock Map参数的匹配问题求助
我明白你遇到的问题了——当你想针对Map中包含特定Person对象作为key的情况,Mock服务方法的返回值时,之前的写法因为参数匹配逻辑不对导致失败。让我一步步帮你理清问题根源和解决办法:
为什么之前的写法不生效?
你尝试的serviceClass.call([(PersonA):_]) >> MockReturnObjectA或者用Map.of的写法,本质上是创建了一个实际的Map对象,其中key是PersonA,value是Spock的通配符匹配器_。但Spock并不会把这个Map当作匹配条件,而是会尝试精确匹配这个Map实例和实际调用时传入的Map——显然这两个不是同一个对象,所以匹配失败,最终返回null。
Spock的参数匹配器需要直接作用在方法调用的参数位置,或者通过专门的匹配器来描述你期望的参数条件,而不是把匹配器塞进一个实际的Map里。
正确的解决方案
方法1:使用argThat自定义匹配逻辑
这是最灵活的方式,你可以编写lambda表达式来检查传入的Map是否符合你的要求:
// 匹配包含PersonA作为key,且对应value是List<Attribute>的Map serviceClass.call(argThat { Map<Person, List<Attribute>> map -> map.containsKey(PersonA) && map.get(PersonA) instanceof List<Attribute> }) >> MockReturnObjectA
这个lambda会接收实际调用时传入的Map,你可以在这里写任何你需要的判断逻辑——比如检查key是否是PersonA,value是否符合类型要求等。
方法2:使用Spock内置的MapContaining匹配器(Spock 2.x+适用)
如果你只需要匹配包含特定key的Map,不管value是什么,可以用Spock提供的MapContaining匹配器,代码更简洁:
import spock.util.matcher.MapContaining serviceClass.call(MapContaining.key(PersonA)) >> MockReturnObjectA
这个匹配器会自动匹配任何以PersonA为key的Map,无需额外的自定义逻辑。
方法3:针对Mock对象的引用匹配(重要!)
因为你的PersonA是Mock(Person.class)生成的实例,Spock默认使用引用相等来比较Mock对象。如果你的测试中,传入entryClass.process的PersonA和你在Mock中指定的是同一个实例,那上面的方法都能正常工作;但如果是不同的实例(即使属性相同),匹配会失败。
这种情况下,你可以用same()匹配器来明确检查引用是否一致:
serviceClass.call(argThat { map -> map.keySet().any { same(it, PersonA) } }) >> MockReturnObjectA
结合你的场景的完整示例
假设你的测试代码结构如下,这里给出完整的正确写法:
def "test entryClass.process with specific Person mocks"() { given: // 创建Person Mock实例 def personA = Mock(Person) def personB = Mock(Person) def personC = Mock(Person) def service = Mock(ServiceClass) def entryClass = new EntryClass(service: service) // 为不同的Person指定不同的返回值 service.call(argThat { it.containsKey(personA) }) >> mockReturnObjectA service.call(argThat { it.containsKey(personB) }) >> mockReturnObjectB service.call(argThat { it.containsKey(personC) }) >> mockReturnObjectC when: entryClass.process([personA, personB, personC]) then: // 验证每个Person对应的服务方法都被调用了一次 1 * service.call(_) 1 * service.call(_) 1 * service.call(_) }
关键总结
- 不要把Spock的匹配器(比如
_)塞进实际的对象(比如Map)里,而是要用匹配器来描述参数的条件。 - 对于Mock对象的匹配,默认是引用相等,确保你在测试中使用的是同一个Mock实例,或者用
same()明确指定引用匹配。 argThat是处理复杂参数匹配的万能工具,几乎能满足所有自定义条件的需求。
内容的提问来源于stack exchange,提问作者pillow

