I've a simple REST API in Spring Boot ( 1.4.0.RELEASE ) which should not be connected to a datasource, and as such, needs to take in an object and use it to return another object ( interface type ) in the same method. This (kind of) unusual behaviour led me to using RequestMethod.POST
to both be able to have @RequestBody and @ResponseBody :
@RestController
public class SomeController {
@RequestMapping(value = "/path", method = RequestMethod.POST)
public ResponseEntity<SomeInterface> someMethod(@RequestBody SomeClass obj) {
SomeInterface toReturn = createSomeInterface(...);
return new ResponseEntity<>(toReturn, HttpStatus.OK);
}
}
I also have a Test
method:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SomeApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SomeControllerTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testSomeMethod() {
SomeClass toPost = createPostObj();
ResponseEntity<SomeInterface> respons = this.restTemplate.postForEntity("/path", toPost, SomeInterface.class);
assertEquals(HttpStatus.OK, respons.getStatusCode());
assertNotEquals(null, respons.getBody());
}
}
However, this throws:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document:
Can not construct instance of SomeInterface:
abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
Assuming both SomeInterface
and SomeClass
are belongings to other API's and can't be changed, is this possible to accomplish?
Note
I've tried to wrap the SomeInterface
into a class without success:
public class Wrapper {
SomeInterface obj;
// getter and setter ...
}
Your controller is correct, the problem lies in your test.
The controller method returns a SomeInterface instance object, which is implemented by some unknown class in the external API you are using. It's probably a POJO or maybe contains Jackson annotations, and it is automatically converted to JSON/XML without problems.
But in the test you are using RestTemplate to call your controller and bind the result to a new object. RestTemplate needs a class, not an interface, to be able to create the new instance and populate it with the data received from your controller in JSON/XML format.
If you don't have access or know the class that implements that interface to use it in your test, you can change it to check for text contained into the response as explained in the "Spring Boot documentation" or use a JSON assertion library if you need something more advanced.
Can not construct instance of SomeInterface: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
Jackson complains about passing SomeInterface.class
as parameter to restTemplate.postForEntity
method, Jackson can not instantiate an interface pass an implementation instead.
Your test should look like
@Test
public void testSomeMethod() {
SomeClass toPost = createPostObj();
ResponseEntity<SomeInterface> respons = this.restTemplate.postForEntity("/path", toPost, SomeInterfaceImpl.class);
assertEquals(HttpStatus.OK, respons.getStatusCode());
assertNotEquals(null, respons.getBody());
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.