Search code examples
groovyspock

Spock: mocked class's method is not being matched


I was able to get a passing test for the dumbed down version of my code (thanks to cgrim! Spock: method not recognized as an invocation), but with the real code it won't work unless the getAssetIdBatch returns something that is non-null. I can't figure out why my interactions aren't being implemented. Below, you can see three attempts to get getAssetIdBatch to return the map1 sample.

Here's a dumbed down version of the code:

class VmExportTaskSplitter implements TaskSplitter<Export> {

    @Inject
    AssetServiceClient assetServiceClient

    @Override
    int splitAndSend(Export export) {

        Map batch = [:]
        Map tags = [:]

        if (true) {
            println('test')
            batch = assetServiceClient.getAssetIdBatch(export.containerUuid,
                    export.userUuid, (String) batch.scrollId, tags)
            print('batch: ')
            println(batch)
        }

        return 1

    }
}

And now the test:

class VmExportTaskSplitterSpecification extends Specification{
    def "tags should be parsed correctly"(){
        setup:
        Export export = new Export(containerUuid: "000", userUuid: "000", chunkSize: 10)
        FilterSet filterSet = new FilterSet()
        filterSet.tag = [:]
        filterSet.tag['tag.Location'] = 'Boston'
        filterSet.tag['tag.Color'] = 'red'
        Map<String, String> expectedTags = ['tag.Location':'Boston', 'tag.Color':'red']
        ObjectMapper mapper = new ObjectMapper()
        export.filters = mapper.writeValueAsString(filterSet)

        def assetServiceClient = Mock(AssetServiceClientImpl) {
            Map map1 = [assetIds:["1","2","3","4","5"],scrollId:null]
            getAssetIdBatch(_ as String,_ as String, null, _ as Map) >> map1
            getAssetIdBatch('000', '000', null, ['tag.Location':'Boston', 'tag.Color':'red']) >> map1
            getAssetIdBatch(_, _, _, _) >> map1
        }

        VmExportTaskSplitter splitter = new VmExportTaskSplitter()
        splitter.assetServiceClient = assetServiceClient

        when:
        splitter.splitAndSend(export)

        then:
        1 * assetServiceClient.getAssetIdBatch(_ as String, _ as String, _, _ as Map)
    }
}

When this is run it can be seen that batch is still being printed as null. What am I doing wrong with setting up the interactions?

Using logging directory: './logs'
Using log file prefix: ''
test
batch: null

Solution

  • You –like so many before– ran into the one giant gotcha of Spock: the combination of Mocking and Stubbing and the fact that it has to happen in one line. Form the docs:

    Mocking and stubbing of the same method call has to happen in the same interaction.

    You stubbed assetServiceClient.getAssetIdBatch to return map1 in your given block and then you verified the mocked call in your then block. The latter one implicitly instructs the mock to return null instead of map1. Think

    1 * assetServiceClient.getAssetIdBatch(_ as String, _ as String, _, _ as Map) // >> null
    

    Change that line to

    1 * assetServiceClient.getAssetIdBatch(_ as String, _ as String, _, _ as Map) >> map1
    

    and define map1 in the method's scope and it will work as expected.

    You probably want to remove the duplicate from the given block as well.

    Don't worry about this being in the then block. Spock executes all mocking and stubbing before you enter the when block. Step through the code if you'd like to see it.