【问题标题】:Stubbed method should return value depending on given mock parameter in Spock存根方法应根据 Spock 中给定的模拟参数返回值
【发布时间】:2026-02-03 06:35:01
【问题描述】:

我希望有不同的返回结果 - 取决于方法的给定模拟参数。请考虑以下代码 sn-p 以遵循我的意图

class ExampleSpec extends Specification {

    def "should return second value of list of return values"() {
        given:
        Person personBob = Mock()
        Person personJackson = Mock()
        PersonHelper stubbedPerson = Stub()

        stubbedPerson.getNameOfBrother(personBob) >> "Billy Bob";
        stubbedPerson.getNameOfBrother(personJackson) >> "Tommy Jackson";

        when:
        String actual = stubbedPerson.getNameOfBrother(personBob)
        String actual2 = stubbedPerson.getNameOfBrother(personJackson)

        then:
        actual == "Billy Bob" // true
        actual2 == "Tommy Jackson" // false "Billy Bob"
    }

}

测试失败,因为对 var actual2 的第二次调用仍然返回 Billy Bob 而不是 Tommy Jackson。我知道有一种方法可以通过调用顺序返回不同的值,但我想让它依赖于给定的模拟。

使用正常值 - 没有 Mock/Stub Proxies - 作为参数值确实有效。我假设 Spock 引擎在两个模拟之间没有区别。但我对此不确定,因为代理确实有 ID 作为实例字段。

【问题讨论】:

    标签: groovy mocking spock stub


    【解决方案1】:

    为了记录 - 使用模拟对象存根有效。我在您的示例中添加了简单的 PersonPersonHelper 类并且测试通过了:

    import spock.lang.Specification
    
    class ExampleSpec extends Specification {
    
        def "should return second value of list of return values"() {
            given:
            Person personBob = Mock()
            Person personJackson = Mock()
            PersonHelper stubbedPerson = Stub()
    
            stubbedPerson.getNameOfBrother(personBob) >> "Billy Bob";
            stubbedPerson.getNameOfBrother(personJackson) >> "Tommy Jackson";
    
            when:
            String actual = stubbedPerson.getNameOfBrother(personBob)
            String actual2 = stubbedPerson.getNameOfBrother(personJackson)
    
            then:
            actual == "Billy Bob" // true
            actual2 == "Tommy Jackson" // false "Billy Bob"
        }
    
        static class Person {
            String name
        }
    
        static class PersonHelper {
            String getNameOfBrother(Person person) {
                return null
            }
        }
    }
    

    我已经用spock-core:1.1-groovy-2.4spock-core:1.0-groovy-2.4 甚至spock-core:0.7-groovy-2.0 进行了检查。一切正常。

    但更重要的是 - 这样的测试没有任何意义。你根本不测试你的代码。您只测试模拟框架是否正确模拟。如果您在生产代码中使用 Spock 模拟,这个测试可能会有意义,但这不是一个有效的假设。

    可能出了什么问题?

    想一想这个测试。根据您的when: 块,您正在尝试测试PersonHelper.getNameOfBrother(Person person) 是否为两个不同的对象返回一个有效的兄弟名称。现在让我们假设这是您项目中对PersonHelper 类的唯一测试。想象一下,如果突然实现 getNameOfBrother 方法开始抛出 NullPointerException 出于某种随机原因会发生什么。问问自己——你的单元测试能保护你免受这种情况的影响吗?没有。您的测试总是通过,因为您正在对正在测试的方法进行存根。如果您改为测试一个真实的实现并传递一个真实的Person 对象,那么您将在测试中收到有关NullPointerException 的通知。否则在部署代码时会看到它,并且某些用户的操作会调用此方法并且它会失败。

    【讨论】: