Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Rest Template mocking with Spock

Can anyone show me an example, how to test RestTemplate using Spock. My classes looks like this:

@Service
public class SomeService {

    @Autowired
    private EndpointUrlProvider endpointUrlProvider;
    private RestTemplate restTemplate = new RestTemplate();

    public SomeResponse doSomePostRequest(HttpEntity<?> httpEntity) throws Exception {
    ResponseEntity<SomeResponse> response;
            try{
                response = restTemplate.postForEntity(endpointUrlProvider.getSomeUrl(),httpEntity,SomeResponse.class);
            } catch (Exception e){
                throw new Exception("Exception occured during post for:" + httpEntity.getBody().getClass().getSimpleName() + " Cause: " + e.getMessage());
            }
            if(response.getStatusCode() == HttpStatus.OK){
                return response.getBody();
            }
            throw new Exception("Error during " + response.getBody().getClass().getSimpleName() + "Http status is diffrent than 200: " + response.getBody().toString());
        }
    }

Tests:

class SomeTest extends Specification {

    RestTemplate restTemplate = Mock {
        postForEntity(_, _, SomeResponse.class) >> new ResponseEntity(new SomeResponse(), HttpStatus.OK)
    }

    @Autowired
    SomeService someService

    def "someTest"() {
        when:
        SomeResponse someResponse = someService.doSomePostRequest(new HttpEntity<>(new SomeBody(), new HttpHeaders()))
        then:
        someResponse == new SomeResponse()
    }
}

The major problem is mocking behaviour of RestTemplate,I looking for solution how to do it in proper way. I'm dont use spring boot.

like image 863
Kamil W Avatar asked Jul 19 '26 18:07

Kamil W


1 Answers

The situation is following:

You create new RestTemplate in your service class.

  private RestTemplate restTemplate = new RestTemplate();

Then you create mock in tests and call your service method:

    RestTemplate restTemplate = Mock {
            postForEntity(_, _, SomeResponse.class) >> new ResponseEntity(new SomeResponse(), HttpStatus.OK)
        }
                  ....
    someService.doSomePostRequest

But your service still has usual rest template inside. You should inject mock object. I would suggest you doing it through constructor. So change your code to:

@Service
public class SomeService {

private EndpointUrlProvider endpointUrlProvider;
private RestTemplate restTemplate;

@Autowired
public SomeService(EndpointUrlProvider endpointUrlProvider, RestTemplate restTemplate){
  this.endpointUrlProvider = endpointUrlProvider;
  this.restTemplate = restTemplate;
}

And your test will be:

class SomeTest extends Specification {

    RestTemplate restTemplate = Mock {
        postForEntity(_, _, SomeResponse.class) >> new ResponseEntity(new SomeResponse(), HttpStatus.OK)
    }

    SomeService someService = new SomeService (  null, restTemplate);

    def "someTest"() {
        when:
        SomeResponse someResponse = someService.doSomePostRequest(new HttpEntity<>(new SomeBody(), new HttpHeaders()))
        then:
        someResponse == new SomeResponse()
    }
}

Now your service object will call method on injected MOCK, not usual RestTemplate

p.s.

Constructor injection considered to be a good practice by spring.

It's better to create a RestTemplate bean and inject it everywhere, than create new object in all services.

like image 114
Ivan Lymar Avatar answered Jul 21 '26 08:07

Ivan Lymar