spock icon indicating copy to clipboard operation
spock copied to clipboard

Spock InteractionBuilder doesn't work with spread (*) operator

Open PavelKovalkov opened this issue 4 years ago • 3 comments

Describe the bug

Unable to pass spread operator (*) to interaction verification.

To Reproduce

I have a service that is just a wrapper for another class:

class WrapperClass {
    InternalClass internalClass;

    public void firstMethod(String firstArg, String secondArg) {
        internalClass.firstMethod(firstArg, secondArg);
    }

    public void secondMethod(String firstArg, String secondArg, long thirdArg) {
        internalClass.secondMethod(firstArg, secondArg, thirdArg);
    }
}
class InternalClass {

    public void firstMethod(String firstArg, String secondArg) {
        // do smth
    }

    public void secondMethod(String firstArg, String secondArg, long thirdArg) {
        // do smth
    }
}

I am writing the following test that dynamically verifies all wrapper methods:

class WrapperClassTest extends Specification {

    def innerClass = Mock(InnerClass)
    def wrapper = new WrapperClass(innerClass: innerClass)

    @Unroll
    def "test #methodName with args #args"() {
        when:
            wrapper."$methodName"(*args)

        then:
            1 * innerClass."$methodName"(*args)

        where:
            methodName      | args
            'firstMethod'        | ['firstArg', 'secondArg']
            'secondMethod'   | ['firstArg', 'secondArg', 123L]
    }
}

But I am getting the following exception:

groovy.lang.MissingMethodException: No signature of method: org.spockframework.mock.runtime.InteractionBuilder.addEqualArg() is applicable for argument types: (java.lang.String, java.lang.String) values: [firstArg, secondArg]
Possible solutions: addEqualArg(java.lang.Object)

Expected behavior

The test should pass.

Actual behavior

Test fails with the following exception:

groovy.lang.MissingMethodException: No signature of method: org.spockframework.mock.runtime.InteractionBuilder.addEqualArg() is applicable for argument types: (java.lang.String, java.lang.String) values: [firstArg, secondArg]
Possible solutions: addEqualArg(java.lang.Object)

Java version

java version "1.8.0_251"

Buildtool version

Gradle 6.5

What operating system are you using

Windows

Dependencies

spock 1.3 - groovy 2.4

Additional context

No response

PavelKovalkov avatar Aug 06 '21 12:08 PavelKovalkov

I am not sure, this is supposed to work, but FWIW, I can confirm that in Spock 2.0 + Groovy 3 the outcome is the same.

kriegaex avatar Aug 06 '21 15:08 kriegaex

Interactions are transformed at compile time and each argument is translated to its own method call, e.g., addEqualArg. That is why this doesn't work at the moment, since it would have to translate [1, 2] to .addEqualArg(1).addEqualArg(2) instead of .addEqualArg(*args) during runtime. Spock only has special handling for the *_ expression, meaning any arguments.

For now you can use a workaround:

class WrapperClassTest extends Specification {

    def innerClass = Mock(InternalClass)
    def wrapper = new WrapperClass(internalClass: innerClass)

    @Unroll
    def "test #methodName with args #args"() {
        when:
            wrapper."$methodName"(*args)

        then:
            1 * innerClass."$methodName"(*_) >> { invocationArgs ->
                assert invocationArgs == args
            }

        where:
            methodName       | args
            'firstMethod'    | ['firstArg', 'secondArg']
            'secondMethod'   | ['firstArg', 'secondArg', 123L]
    }
}

leonard84 avatar Aug 07 '21 08:08 leonard84

Thanks, It would help.

PavelKovalkov avatar Aug 09 '21 12:08 PavelKovalkov