SudeepShakya
SudeepShakya

Reputation: 621

Using TestRestTemplate for Spring Integration Testing

I am learning Spring Integration testing and using org.springframework.boot.test.web.client.TestRestTemplate. My Spring Boot version is 2.0.2 and with some default configuration.

My Test class is :

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {

    @Autowired
    TestRestTemplate restTemplate;      

    String userBaseUrl = "/api/user/";

    UserDto userDto;

    @Before
    public void setUp() {

        EnhancedRandom er = EnhancedRandomBuilder.aNewEnhancedRandomBuilder().collectionSizeRange(1, 1).build();

        userDto = er.nextObject(UserDto.class);     

    }

    @Test
    public void registerUser() throws Exception{

        ResponseEntity<ServiceResponse> responseEntity = restTemplate.postForEntity(userBaseUrl+"register_user", userDto, ServiceResponse.class);
        assertEquals(responseEntity.getStatusCode(), HttpStatus.OK);
    }
    }

And controller endpoint method is :

@RequestMapping(value="register_user",method = RequestMethod.POST)  
    public ResponseEntity<ServiceResponse> registerUser(@RequestHeader HttpHeaders headers, @RequestBody UserDto userDto) {

        log.info("Register user");

        try {           
            ServiceResponse serviceResponse = new ServiceResponse("Users listed successfully"); 

            serviceResponse.setData(userService.registerUser(userDto));

            return new ResponseEntity<ServiceResponse>(serviceResponse,HttpStatus.OK);

        } catch (Exception e) {
            GeneralUtil.logError(log, "Error Occurred listing users", e);
            HttpHeaders httpHeaders = ServiceResponse.generateRuntimeErrors(e);
            return new ResponseEntity<>(httpHeaders, HttpStatus.EXPECTATION_FAILED);
        }
    }

And ServiceResponse is :

public class ServiceResponse {

    private Boolean success;
    private String message;
    private Map<String, Object> params;
    private List<?> list;
    private Object data;
//getters,setters.
    }

This API endpoint works well when we test manually but doesn't work while running tests.It gives this error :

org.springframework.web.client.RestClientException: Error while extracting response for type [class com.eeposit.lattice.util.ServiceResponse] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.eeposit.lattice.util.ServiceResponse` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.eeposit.lattice.util.ServiceResponse` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 1, column: 2]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:115)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:991)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:974)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:725)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:680)
    at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:466)
    at org.springframework.boot.test.web.client.TestRestTemplate.postForEntity(TestRestTemplate.java:502)
    at com.eeposit.lattice.controller.UserControllerIntegrationTest.registerUser(UserControllerIntegrationTest.java:61)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:539)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:761)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:461)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:207)
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.eeposit.lattice.util.ServiceResponse` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.eeposit.lattice.util.ServiceResponse` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 1, column: 2]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:241)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:100)
    ... 39 more
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.eeposit.lattice.util.ServiceResponse` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 1, column: 2]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1031)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1290)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235)
    ... 41 more

I tried to solve this issue googling but cannot fix the issue.

Upvotes: 3

Views: 5141

Answers (2)

SudeepShakya
SudeepShakya

Reputation: 621

I solved this problem. I did some modification in the code. I added this :

public HttpHeaders setHeaders(String accesstoken) {

        List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
        acceptableMediaTypes.add(MediaType.APPLICATION_JSON);

        HttpHeaders headers = new HttpHeaders();  
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(acceptableMediaTypes);
        headers.set("accesstoken", accesstoken);
        return headers;

    }

And changed the test method to this :

@Test
    public void registerUser() throws Exception{

        super.getLog().info("Register user integration test.");

        HttpHeaders headers = setHeaders(null);
        HttpEntity<UserDto> entity = new HttpEntity<UserDto>(userDto,headers);

        ResponseEntity<String> responseEntity = restTemplate.exchange(userBaseUrl+"register_user", HttpMethod.POST, entity, String.class);  

        assertEquals(responseEntity.getStatusCode(), HttpStatus.OK);
        assertNotNull(responseEntity.getBody());    

        ServiceResponse serviceResponse = super.gson.fromJson(responseEntity.getBody(), ServiceResponse.class);     

        Map<String, Object> data = (Map<String, Object>) serviceResponse.getData();

        assertTrue(data.containsKey("accesstoken"));

        assertTrue(data.containsKey("user"));
    }

Instead of using this :

ResponseEntity<ServiceResponse> responseEntity = restTemplate.postForEntity(userBaseUrl+"register_user", userDto, ServiceResponse.class);

Upvotes: 2

GaborH
GaborH

Reputation: 719

Your problem could be caused by that you get some error response which can't be parsed as ServiceResponse. Try to change the ResponseEntity type to String instead of the ServiceResponse type.

ResponseEntity<String> responseEntity = restTemplate.postForEntity(userBaseUrl+"register_user", userDto, String.class);
System.out.println("This is the response as a String" + responseEntity.getBody());

I believe that way you will be able to see what the call returns.

Upvotes: 5

Related Questions