使用MockMultipartFile

时间:2015-11-12 08:38:26

标签: spring spring-mvc spring-boot spring-test spring-test-mvc

我使用Spring Boot创建文件上传服务,并使用Spring Mock Mvc和MockMultipartFile进行测试。我想测试是否在超出最大文件大小时抛出错误。以下测试失败,因为它收到200。

RandomAccessFile f = new RandomAccessFile("t", "rw");
f.setLength(1024 * 1024 * 10);
InputStream is = Channels.newInputStream(f.getChannel());

MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", is);

mvc.perform(fileUpload("/files")
    .file(firstFile))
    .andExpect(status().isInternalServerError());

是否有可能测试上传文件的大小?

3 个答案:

答案 0 :(得分:5)

根据documentation

  

如果length方法返回的文件的当前长度是   小于newLength参数,然后文件将被扩展。在   在这种情况下,文件的扩展部分的内容不是   定义

请改为尝试:

byte[] bytes = new byte[1024 * 1024 * 10];
MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", bytes);

答案 1 :(得分:1)

不能用 Spring 的 MockMultipartFile / MockMvc 测试这个。这样做的原因是错误的根源不在 Spring 本身,而是在底层 Web 服务器(通常是 Tomcat)中,正如您在 MaxUploadSizeExceededException 的堆栈跟踪中所见:

org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size of 500000 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1065736) exceeds the configured maximum (500000)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:160)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
[...]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1065736) exceeds the configured maximum (500000)
    at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:965)
    at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)
    at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)
    at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:156)
    ... 20 more

当使用默认的 MockMvc 和 @SpringBootTest 的默认设置时,不会启动真正的 Web 服务器,因此不会发生错误。

然而,您可以通过提供 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 来告诉 Spring 为您的测试启动一个真正的 Web 服务器,这将(意外)在一个随机端口上启动一个 Web 服务器。您可以在测试类中使用 @LocalServerPort 访问该端口。

然后您可以编写一个测试,对您的测试服务器执行真实分段上传,而不是伪造的。 REST Assured 是一个可以执行此操作的库:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyTest {

    @LocalServerPort
    private int port;

    @Test
    void testMultipartUpload() throws Exception {
        File file = new File("my-file");
        RestAssured.baseURI = "http://localhost/api";
        RestAssured.port = port;
        Response res = given()
                .multiPart("data", file, "text/plain")
                .when().post("/upload");
        ...
    }

}

当您上传的文件太大时,此测试将显示服务器错误。

答案 2 :(得分:0)

这里有同样的问题。

不知道是否有更好的解决方案,但是我创建了一个注释来验证上传的图像列表,并与其他图像一起进行检查。

  • 验证图像的类
    public class ImageValidator implements ConstraintValidator<ValidImage, List<MultipartFile>> {

      @Override
      public boolean isValid(
          List<MultipartFile> listMultipartFile, ConstraintValidatorContext context) {

        for (var multipartFile : listMultipartFile) {
          var msgValidation = imageValidations(multipartFile);

          if (!msgValidation.isEmpty()) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(msgValidation).addConstraintViolation();
            return false;
          }
        }

        return true;
      }

      private String imageValidations(MultipartFile multipartFile) {
        var contentType = multipartFile.getContentType();

        if (!isSupportedContentType(contentType)) {
          return String.format(
              "Only JPG and PNG images are allowed, %s provided.", multipartFile.getContentType());
        } else if (multipartFile.isEmpty()) {
          return "It must not be an empty image.";
        } else if (multipartFile.getSize() > (1024 * 1024)) {
          return "File size should be at most 1MB";
        }

        return "";
      }

      private boolean isSupportedContentType(String contentType) {
        var supportedContents = List.of("image/jpg", "image/jpeg", "image/png");
        return supportedContents.contains(contentType);
      }
    }
  • 接口
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {ImageValidator.class})
    public @interface ValidImage {
      String message() default "Invalid image file";

      Class<?>[] groups() default {};

      Class<? extends Payload>[] payload() default {};
    }