Search code examples
spring-mvcmockingspock

Mocking RestOperations.exchange in Spock (overloaded method with varargs)


I'm trying to mock org.springframework.web.client.RestOperations.exchange in Spock. Spock fails with

Too few invocations for:

1 * restOperations.exchange("https://test.com", HttpMethod.POST, _ as HttpEntity, String)   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * restOperations.exchange('https://test.com', POST, <whatever,[]>, class java.lang.String, [])

I think the problem is related to the fact that the exchange method is overloaded and the version I'm trying to call has vararg arguments.

How do I define this interaction, so the test succeeds?

MySubject.java:


import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestOperations;

public class MySubject {
    private final RestOperations rest;

    public MySubject(RestOperations rest) {
        this.rest = rest;
    }

    public void doStuff() {
        HttpEntity<String> httpEntity = new HttpEntity<>("whatever");
        rest.exchange("https://test.com", HttpMethod.POST, httpEntity);
    }
}

MyTest.groovy:


import org.apache.http.HttpEntity
import org.springframework.http.HttpMethod
import org.springframework.web.client.RestOperations
import spock.lang.Specification

class MyTest extends Specification {
    RestOperations restOperations = Mock(RestOperations)
    MySubject subject = new MySubject(restOperations)

    def "test"() {
        when:
        subject.doStuff()
        then:
        1 * restOperations.exchange("https://test.com", HttpMethod.POST, _ as HttpEntity, String)
    }
}

Solution

  • You have multiple problems:

    1. In the application you import org.springframework.http.HttpEntity, in the test org.apache.http.HttpEntity. You need to correct that.

    2. The call rest.exchange("https://test.com", HttpMethod.POST, httpEntity); in your application does not even compile because there is no such signature in the RestOperations class. You need to add the parameter String.class.

    3. In the test you need to reflect the method signature including the varargs, i.e. the real method signature has 5 arguments.

    If you fix all of these your test runs smoothly:

    package de.scrum_master.stackoverflow.q61135628;
    
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpMethod;
    import org.springframework.web.client.RestOperations;
    
    public class MySubject {
      private final RestOperations rest;
    
      public MySubject(RestOperations rest) {
        this.rest = rest;
      }
    
      public void doStuff() {
        HttpEntity<String> httpEntity = new HttpEntity<>("whatever");
        rest.exchange("https://test.com", HttpMethod.POST, httpEntity, String.class);
      }
    }
    
    package de.scrum_master.stackoverflow.q61135628
    
    import org.springframework.http.HttpMethod
    import org.springframework.web.client.RestOperations
    import spock.lang.Specification
    
    class MyTest extends Specification {
      RestOperations restOperations = Mock()
      MySubject subject = new MySubject(restOperations)
    
      def "test"() {
        when:
        subject.doStuff()
    
        then:
        1 * restOperations.exchange("https://test.com", HttpMethod.POST, _, String, _)
        // Or if you want to be more specific:
    //  1 * restOperations.exchange("https://test.com", HttpMethod.POST, _, String, [])
      }
    }