Search code examples
spock

Spock check invocation using variable instead of literal


I have a Spock test like this:

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"
        ]))
    }
}

This works fine but I'd prefer to change the assertion to

1 * snsClient.publish(topicArn, message)

to reduce duplication of the map literal. When I do I get the following error:

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"}

Why is the second argument coming through as null when I use the message variable instead of a map literal? The first argument is a variable rather than a literal and it is coming through fine.


Solution

  • I'd prefer to change the assertion to

    1 * snsClient.publish(topicArn, message)
    

    Well, it cannot work like that. As your posted test error implies, what you really want is

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

    which leads to the exception you see during the test.

    The reason is that the interaction you are testing in the then: block must be prepared to be checked before the code in the when: block gets executed. But before that block message is null. So you want to assign the value in a setup: or given: (both are just aliases for each other) block:

    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))
    }
    

    Now your test passes.