使用具有独立上下文和SpringBoot 1.2.5的MockMvcBuilders进行文件上载的单元测试

时间:2015-08-30 05:39:47

标签: java spring unit-testing spring-mvc multipartform-data

我正在使用Spring Boot 1.2.5-RELEASE。我有一个接收MultipartFileString

的控制器
@RestController
@RequestMapping("file-upload")
public class MyRESTController {

  @Autowired
  private AService aService;

  @RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  @ResponseStatus(HttpStatus.CREATED)
  public void fileUpload(
      @RequestParam(value = "file", required = true) final MultipartFile file,
      @RequestParam(value = "something", required = true) final String something) {
   aService.doSomethingOnDBWith(file, value);
  }
}

现在,该服务运作良好。我用PostMan进行了测试,eveything正如预期的那样。 不幸的是,我无法为该代码编写独立的单元测试。目前的单元测试是:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@WebAppConfiguration
public class ControllerTest{

    MockMvc mockMvc;

    @Mock
    AService aService;

    @InjectMocks
    MyRESTController controller;

  @Before public void setUp(){
    MockitoAnnotations.initMocks(this);
    this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
  }

  @Test
  public void testFileUpload() throws Exception{
        final File file = getFileFromResource(fileName);
        //File is correctly loaded
        final MockMultipartFile multipartFile = new MockMultipartFile("aMultiPartFile.txt", new FileInputStream(file));

        doNothing().when(aService).doSomethingOnDBWith(any(MultipartFile.class), any(String.class));

        mockMvc.perform(
                post("/file-upload")
                        .requestAttr("file", multipartFile.getBytes())
                        .requestAttr("something", ":(")
                        .contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
                .andExpect(status().isCreated());
    }
}

测试失败,

java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?

现在,在Spring Boot的MultipartAutoConfiguration类中,我看到MultipartResolver是自动配置的。但是,我认为standaloneSetup的{​​{1}}我无法访问此内容。

我尝试了几种单元测试配置,为简洁起见我没有报告。特别是,我也尝试了如here所示的放心,但老实说这不起作用,因为我似乎无法模仿MockMvcBuilders实例。

任何解决方案?

3 个答案:

答案 0 :(得分:1)

您正在尝试将此单元测试(standaloneSetup(controller).build();)与Spring集成测试(@RunWith(SpringJUnit4ClassRunner.class))结合使用。

做其中一个。

  1. 集成测试需要使用类似下面的代码。问题是假装豆子。有一些方法可以使用@Primary注释和@Profile注释伪造这样的bean(您创建将覆盖主生产bean的测试bean)。我有一些关于Spring bean伪造的例子(例如this bean中的this bean替换为this test

    @Autowired
    private WebApplicationContext webApplicationContext;
    
    @BeforeMethod
    public void init() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }
    
  2. Secodn选项是删除测试和测试控制器上的@RunWith(SpringJUnit4ClassRunner.class)和其他类级配置,而不使用独立安装的Spring Context。这样您就无法在控制器上测试验证注释,但您可以使用Spring MVC注释。优势是可以通过Mockito伪豆(例如通过InjectMocks和Mock注释)

答案 1 :(得分:1)

我混合了lkrnak建议的内容和Mockito @Spy的功能。我使用REST-Assured来进行呼叫。所以,我做了如下:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@WebAppConfiguration
@IntegrationTest({"server.port:0"})
public class ControllerTest{

    {
      System.setProperty("spring.profiles.active", "unit-test");
    }


    @Autowired
    @Spy
    AService aService;

    @Autowired
    @InjectMocks
    MyRESTController controller;

    @Value("${local.server.port}")
    int port;    


  @Before public void setUp(){
    RestAssured.port = port;

    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void testFileUpload() throws Exception{
        final File file = getFileFromResource(fileName);

        doNothing().when(aService)  
             .doSomethingOnDBWith(any(MultipartFile.class), any(String.class));

        given()
          .multiPart("file", file)
          .multiPart("something", ":(")
          .when().post("/file-upload")
          .then().(HttpStatus.CREATED.value());
    }
}

服务定义为

@Profile("unit-test")
@Primary
@Service
public class MockAService implements AService {
  //empty methods implementation
}

答案 2 :(得分:0)

错误表明请求不是多部分请求。换句话说,在那时它预计会被解析。但是在MockMvc测试中没有实际的请求。这只是模拟请求和响应。所以你需要使用perform.fileUpload(...)来设置模拟文件上传请求。