Spock框架Stubbing未按预期工作:存根方法返回null而非模拟响应
Spock框架Stubbing未按预期工作:存根方法返回null而非模拟响应
我来帮你排查这个问题,你的测试代码里有几个关键细节没处理对,导致存根没生效,咱们一步步来解决:
问题根源分析
- 存根位置错误:你把存根逻辑(
>> mockResponse)放在了then块里,但Spock的执行顺序是given→when→then,当when块执行serviceA.methodUnderTest时,then块的存根还没生效,所以repositoryA.someMethod会返回默认的null。 - 测试参数未定义:你的测试方法里直接用了
arg1和arg2,但这些变量既没赋值也没通过where块提供数据,参数不匹配的话,Spock找不到对应的存根,同样会返回null。 - @Shared注解的不合理使用:mock对象用
@Shared会在多个测试方法间复用状态,容易导致测试隔离性问题,除非你明确需要保留状态,否则完全没必要。
修正后的测试代码
class ServiceATest extends Specification { // 去掉@Shared,每个测试方法创建独立的mock实例,保证测试隔离 RepositoryA repositoryA = Mock(RepositoryA) ServiceB serviceB = Mock(ServiceB) ServiceA serviceA def "test methodUnderTest when repository returns non-null"() { given: // 初始化被测服务 serviceA = new ServiceA(repositoryA, serviceB) // 明确测试参数 String testArg1 = "demoArg1" String testArg2 = "demoArg2" // 构建模拟响应 ObjectA mockResponse = ObjectA.builder() .field1("demo") .field2("demo") .field3(Instant.now().plus(3, ChronoUnit.DAYS)) .build() // 提前设置存根,确保调用被测方法前生效 1 * repositoryA.someMethod(testArg1, testArg2) >> mockResponse when: serviceA.methodUnderTest(testArg1, testArg2) then: // then块仅做交互验证(可选),比如验证后续逻辑的行为 // 示例:如果else分支不会调用serviceB,可以加这个验证 0 * serviceB._ } }
额外注意事项
如果需要匹配任意参数(比如测试不同参数组合),可以用Spock的参数匹配器:
// 匹配任意两个String类型参数 1 * repositoryA.someMethod(_ as String, _ as String) >> mockResponse // 精确匹配特定值 1 * repositoryA.someMethod(eq("demoArg1"), eq("demoArg2")) >> mockResponse
另外,确保你的ObjectA.Builder能正确构建非null对象,避免因为Builder本身的问题导致模拟响应异常。
备注:内容来源于stack exchange,提问作者Ravish Khatri
相关产品推荐
相关产品推荐

