英文:
Spring Boot REST Controller Integration Test returns 406 instead of 500
问题
我有一个带有生成压缩数据字节流的端点的控制器。下面的代码中的 ``MyDTO`` 和 ``ZipService`` 类是自定义类,它们作为一个普通的POJO,我想将它们的内容添加到压缩文件中,并且有一个服务会将POJO的字节写入 ``ZipOutputStream``,然后通过端点包装在带有适当的 ``HttpStatus`` 和标头的 ``ResponseEntity`` 对象中提供。 “正常情况” 运行良好,并且按预期生成 zip 文件。
```java
@GetMapping(path = "/{id}/export", produces = "application/zip")
public ResponseEntity<byte[]> export(@ApiParam(required = true) @PathVariable(value = "id") String id) throws IOException {
try {
MyDTO myDTO = myService.getDTO(id);
byte[] zippedData = zipService.createZip(myDTO.getBytes());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"name.zip\"");
return new ResponseEntity<>(zipDTO.getData(), httpHeaders, HttpStatus.OK);
} catch (ZipException e) {
return new ResponseEntity(e.getRestError(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
问题出现在我的集成测试类中,当我想要测试自定义 ZipException
被抛出的情况时(如果压缩数据存在问题,可能会发生这种情况)。我们组织内必须遵守的一个标准是,每个自定义异常都需要扩展 Exception
并具有称为 RestError 的自定义对象,其中包含表示自定义错误代码和消息的字符串变量。
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RestError {
private String code;
private String message;
//构造函数
//Getter和Setter
}
这个对象似乎会导致集成测试出现问题。
@Test
public void myIntegrationTest() throws Exception {
MyDTO myDTO = new MyDTO();
RestError restError = new RestError("Custom error code", "Custom error message");
ZipException zipException = new ZipException(restError);
given(myService.getDTO("id")).willReturn(myDTO);
given(zipService.createZip(any(), any())).willThrow(zipException);
mockMvc.perform(get("/{id}/export", "id").accept(MediaType.ALL)
.andDo(print())
.andExpect(status().is5xxServerError());
}
在这种情况下,我期望得到一个 HTTP 状态码为 500,但是 MockMvc
却得到了一个 406 - 内容不可接受的 HTTP 状态码。我已经尝试过让测试接受和期望任何/所有数据,但它仍然每次都返回 406 错误。我知道这与异常的 RestError 对象有关,因为如果我从控制器返回的 ResponseEntity
中去掉它,那么就会返回期望的响应状态。对此的任何帮助都将不胜感激。
<details>
<summary>英文:</summary>
I have a controller with an endpoint that produces a byte stream of zipped data. The ``MyDtO`` and ``ZipService`` classes in the code below are custom classes that function as a POJO whose contents I want to add to the zip and a service that will take the bytes of the POJO and write them to a ``ZipOutputStream`` which will then be made available via the endpoint wrapped in a ``ResponseEntity`` object with the appropriate ``HttpStatus`` and headers. The “happy path” is working fine and is producing the zip file as expected.
@GetMapping(path = "/{id}/export", produces = "application/zip")
public ResponseEntity<byte[]> export(@ApiParam(required = true) @PathVariable(value = "id") String id) throws IOException {
try {
MyDTO myDTO = myService.getDTO(id);
byte[] zippedData = zipService.createZip(myDTO.getBytes());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=""name.zip"");
return new ResponseEntity<>(zipDTO.getData(), httpHeaders, HttpStatus.OK);
} catch (ZipException e) {
return new ResponseEntity(e.getRestError(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
The problem is in my integration test class when I want to test the case where a custom ``ZipException`` is thrown (which could happen if there is a problem with the data being zipped). One of the standards we have to adhere to in our organization is that each custom exception needs to extend ``Exception`` and have a custom object called RestError that has String variables that represent custom error codes and messages.
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RestError {
private String code;
private String message;
//Constructors
//Getters and setters
}
This object seems to cause a problem with the integration tests
@Test
public void myIntegrationTest() throws Exception {
MyDTO myDTO = new MyDTO();
RestError restError = new RestError("Custom error code", "Custom error message")
ZipException zipException = new ZipException(restError);
given(myService.getDTO("id")).willReturn(myDTO);
given(zipService.createZip(any(), any())).willThrow(zipException);
mockMvc.perform(get("/{id}/export", "id").accept(MediaType.ALL)
.andDo(print())
.andExpect(status().is5xxServerError());
}
I would expect a ``HttpStatus`` of 500 in that case but the ``MockMvc`` is hitting a ``HttpStatus`` of 406 - content unacceptable. I’ve messed around with the test so that it can accept and expect any/all data but it still hits that 406 error every time. I know it’s do do with the RestError object of the exception because if I take that out of the ``ResponseEntity`` that is returned by the controller then the expected response status is returned. Any help on this is appreciated.
</details>
# 答案1
**得分**: 2
从 ``@GetMapping(path = "/{id}/export", produces = "application/zip")`` 中删除 produces 部分,
并将其修改为 ``@GetMapping(path = "/{id}/export")``。
由于在测试用例执行期间会返回 406 错误,
如果删除此部分,您将获得您正在抛出的确切错误。
但是,请检查是否如预期般下载了 zip 文件。
<details>
<summary>英文:</summary>
Remove the produces from ``@GetMapping(path = "/{id}/export", produces = "application/zip")``
and change it to ``@GetMapping(path = "/{id}/export")``
Due to this during test case execution 406 error is returned
You will get exact error what you are throwing if you remove this.
However check the zip file gets downloaded as expected or not.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论