You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spock验证Mock时触发MissingPropertyException问题求助

在Spock中验证Mock调用时使用动态生成的返回值

你遇到的问题是Spock的交互验证机制特性导致的——和Mockito不同,Spock的then块里的交互断言(比如1 * listener.onEvent(message))是在测试执行之前就被静态解析的,这时候when块里的局部变量message还未被定义,所以会抛出MissingPropertyException。

下面给你两种可行的解决方案:

方案1:提前声明变量扩大作用域

把message变量的声明移到测试方法的作用域顶部(比如given块中),这样Spock在解析then块时能识别到这个变量,执行时会使用它的实际值进行验证:

import spock.lang.Specification
class MockingTest extends Specification {
    private MyListener listener = Mock()
    def 'test listener called'() {
        given:
        def message // 提前声明变量,扩大作用域
        when:
        def service = new MyService(listener)
        message = service.foo() // 赋值
        then:
        1 * listener.onEvent(message) // 现在可以正常引用变量
    }
    class MyService {
        private MyListener listener;
        MyService(MyListener listener) {
            this.listener = listener
        }
        String foo() {
            String message = "Hello " + new Random().nextInt(10);
            listener.onEvent(message)
            return message;
        }
    }
    class MyListener {
        void onEvent(String message) {
            System.out.println(message);
        }
    }
}

方案2:使用ArgumentCaptor捕获参数

如果你更习惯Mockito的参数捕获方式,Spock也支持ArgumentCaptor,通过捕获Mock调用时的参数来和返回值做对比:

import spock.lang.Specification
import org.spockframework.mock.ArgumentCaptor

class MockingTest extends Specification {
    private MyListener listener = Mock()
    def 'test listener called with argument captor'() {
        given:
        def messageCaptor = ArgumentCaptor.forClass(String)
        when:
        def service = new MyService(listener)
        def message = service.foo()
        then:
        // 捕获调用时传入的参数
        1 * listener.onEvent(messageCaptor.capture())
        // 验证捕获的参数和方法返回值一致
        messageCaptor.value == message
    }
    class MyService {
        private MyListener listener;
        MyService(MyListener listener) {
            this.listener = listener
        }
        String foo() {
            String message = "Hello " + new Random().nextInt(10);
            listener.onEvent(message)
            return message;
        }
    }
    class MyListener {
        void onEvent(String message) {
            System.out.println(message);
        }
    }
}

差异原因说明

Spock的交互验证采用声明式语法,会在测试启动前就解析所有交互断言以确保语法合规;而Mockito的验证是命令式的,只有执行到验证语句时才会处理变量。这就是为什么你在Mockito中可以直接使用局部变量,但Spock需要调整变量作用域或使用参数捕获器的核心原因。

内容的提问来源于stack exchange,提问作者GreenTea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 21:28:15