使用变量而不是文字的 Spock 检查调用

Spock check invocation using variable instead of literal

我有这样的 Spock 测试:

class SomeSpec extends Specification {

    Map message

    String topicArn = "aws::fake-sns-topic"

    def 'test new message'() {
        when: 'A new message is received'
        message = [
                "ownerToken": null,
                "status": "NEW"
        ]
        def response = api.doHandleRequest(message)

        then: 'It is handled'
        1 * snsClient.publish(topicArn, JsonOutput.toJson([
                "ownerToken": null,
                "status": "NEW"
        ]))
    }
}

这很好用,但我更愿意将断言更改为

1 * snsClient.publish(topicArn, message)

减少地图文字的重复。当我这样做时,出现以下错误:

Too few invocations for:

1 * snsClient.publish(topicArn, JsonOutput.toJson(message))   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * snsClient.publish('aws::fake-sns-topic', '{"ownerToken":null","status":"NEW"}')
One or more arguments(s) didn't match:
0: <matches>
1: argument == expected
   |        |  |
   |        |  null
   |        false
   {"ownerToken":null,"status":"NEW"}

当我使用消息变量而不是地图文字时,为什么第二个参数为空?第一个参数是一个变量而不是一个字面量,它通过得很好。

I'd prefer to change the assertion to

1 * snsClient.publish(topicArn, message)

嗯,它不能那样工作。正如您发布的测试错误所暗示的那样,您真正想要的是

1 * snsClient.publish(topicArn, JsonOutput.toJson(message))

这会导致您在测试期间看到异常。

原因是您在 then: 块中测试的交互必须准备好在 when: 块中的代码获取之前进行检查执行。但在那块之前 messagenull。所以你想在 setup:given: (两者只是彼此的别名)块中分配值:

def 'new message gets published'() {
  given: 'a new message'
  message = [
    "ownerToken": null,
    "status"    : "NEW"
  ]

  when: 'the message is received'
  def response = api.doHandleRequest(message)

  then: 'it gets published'
  1 * snsClient.publish(topicArn, JsonOutput.toJson(message))
}

现在你的测试通过了。