首页 > 解决方案 > 看到 Spock 的奇怪行为和从 AWS API Gateway SDK 生成的代码

问题描述

在模拟通过 AWS API Gateway (SDK) 生成的对象时,我遇到了一个异常行为,这很可能是一个 Spock 错误。

例如给定以下测试用例:

def "testing api gateway"() {
  given:
  def sdk = Mock(MyAWSSDK.class)
  sdk.lookupByField("xyz") >> "result"

  when:
  def myClass = newClass(sdk)
  myClass.foo()

  then:
  1 * sdk.lookupByField(_)
}

在我的例子中,当测试用例中出现“1 * sdk.lookupByField( )”时,我得到空指针异常。如果我将其取出并将 then: 条件替换为“true”。测试用例运行没有问题。我还可以在 when: 阶段“sdk.lookupByField(null)”中添加一个附加行,并将“1 * sdk.lookupByField( )”添加回“then:”阶段。它似乎工作。然而,这并不理想。

这是 Spock 的一种奇怪行为,似乎与 AWS API Gateway 生成的代码有关。

我尝试了 JDK 版本 1.8.0_202 和 11.0.1

Maven依赖:

<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.2.10</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.4.4</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.9.9</version>
</dependency>

我计划用 Spock 创建一个错误,但想联系社区,看看是否可以复制。如果那里有任何接受者,将不胜感激。

标签: spock

解决方案


首先,您正在展示一个测试,但没有正在测试的课程。下次您在 SO 上提出问题时,请提供完整的MCVE,因为为了重现您的问题,我必须自己创建它,即使这本来是您的工作。谢谢你。

没有 Spock 错误,但您的测试代码中有两个错误:

  • 应该怎么newClass(sdk)做?我假设您想在那里创建一个MyClass对象,并且可能更想编写def myClass = new MyClass(sdk).
  • 正如Spock 手册所解释的,如果你想结合 mocking 和 stubbing,它们必须发生在同一个交互中。你的假设,你可以先写sdk.lookupByField("xyz") >> "result",然后再写,1 * sdk.lookupByField(_)以便先设置存根返回值,然后再设置预期调用的数量,因此是错误的。您必须在同一个地方定义两者。否则,模拟方法将返回null(默认值),而不是"result",因为第一个语句被第二个语句覆盖。

您的解决方案如下所示:

package de.scrum_master.stackoverflow.q54636103

class MyAWSSDK {
  String lookupByField(String s) {
    "real result"
  }
}
package de.scrum_master.stackoverflow.q54636103

class MyClass {
  MyAWSSDK myAWSSDK

  MyClass(MyAWSSDK myAWSSDK) {
    this.myAWSSDK = myAWSSDK
  }

  String foo(String s) {
    myAWSSDK.lookupByField("xyz")
  }
}
package de.scrum_master.stackoverflow.q54636103

import spock.lang.Specification

class ApiGatewayTest extends Specification {
  def "testing api gateway the wrong way"() {
    given:
    def sdk = Mock(MyAWSSDK)
    sdk.lookupByField("xyz") >> "mock result"

    when:
    def myClass = new MyClass(sdk)
    def result = myClass.foo()

    then:
    1 * sdk.lookupByField(_)
    result == "mock result"    // uh-oh!
  }

  def "testing api gateway the right way"() {
    given:
    def sdk = Mock(MyAWSSDK)

    when:
    def myClass = new MyClass(sdk)
    def result = myClass.foo()

    then:
    1 * sdk.lookupByField("xyz") >> "mock result"
    result == "mock result"    // yeah!
  }
}

第一个特征方法会失败如下,第二个会通过:

Condition not satisfied:

result == "mock result"    // uh-oh!
|      |
null   false

推荐阅读