测试Spring MultipartHttpServletRequest

时间:2013-10-02 15:01:29

标签: java spring spring-mvc

尝试测试我们用于多个文件上传的弹簧控制器。这是控制器:

@RequestMapping("/vocabularys")
@Controller
public class VocabularyController {
...

我想测试的动作:

@RequestMapping(value = "/import", method = {RequestMethod.PUT, RequestMethod.POST})
@ResponseBody
@CacheEvict(value="vocabulary", allEntries=true)
public Object importVocabulary(MultipartHttpServletRequest request, HttpServletResponse response) {
...

我在webmvc-config.xml中的解析器:

<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>

代码工作得很好。当我试图进行单元/集成测试时,我遇到了问题。

以下是我对测试的尝试:

public class VocabularyControllerTest extends BaseControllerTest {

    static final private String AdminUsername = "administrator";

    @Test
    public void shouldBeAbleToUploadAFile() throws Exception {
        createTestWorkspace();
        login(AdminUsername, "*");

        MockMultipartFile file = new MockMultipartFile("test_vocab.xml", new FileInputStream("src/test/files/acme_vocabulary.xml"));
        MockMultipartHttpServletRequestBuilder mockMultipartHttpServletRequestBuilder = (MockMultipartHttpServletRequestBuilder) fileUpload("/vocabularys/import").accept(MediaType.ALL).session(httpSession);
        mockMultipartHttpServletRequestBuilder.file(file);
        mockMultipartHttpServletRequestBuilder.content("whatever");

        ResultActions resultActions = mockMvc.perform(mockMultipartHttpServletRequestBuilder);

        resultActions.andExpect(status().isFound());
    }
}

忽略createWorkspace()login()和东西 - 这些是用于通过一些安全过滤器。

BaseControllerTest的相关部分:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
    @ContextConfiguration(locations = {
        "file:src/test/resources/META-INF/spring/applicationContext.xml",
        "file:src/test/resources/META-INF/spring/applicationContext-security.xml",
        "file:src/main/resources/META-INF/spring/applicationContext-database.xml",
        "file:src/main/resources/META-INF/spring/applicationContext-activiti.xml",
        "file:src/main/resources/META-INF/spring/applicationContext-cache.xml",
        "file:src/main/resources/META-INF/spring/applicationContext-jms.xml",
        "file:src/main/resources/META-INF/spring/applicationContext-mail.xml",
        "file:src/main/resources/META-INF/spring/applicationContext-mongo.xml"}),
    @ContextConfiguration(locations = {
        "file:src/main/webapp/WEB-INF/spring/webmvc-config.xml",
        "file:src/test/webapp/WEB-INF/spring/applicationContext-filters.xml"})
})
@Transactional
public class BaseControllerTest extends BaseTest {

    @Autowired
    WebApplicationContext wac;
    @Autowired
    MockHttpSession httpSession;
    @Autowired
    MockServletContext servletContext;
    @Autowired
    OpenEntityManagerInViewFilter openEntityManagerInViewFilter;
    @Autowired
    HiddenHttpMethodFilter hiddenHttpMethodFilter;
    @Autowired
    CharacterEncodingFilter characterEncodingFilter;
    @Autowired
    SessionFilter sessionFilter;
    @Autowired
    WorkflowAsSessionFilter workflowAsSessionFilter;
    @Autowired
    FilterChainProxy springSecurityFilterChain;
    @Autowired
    RequestFilter requestFilter;
    MockMvc mockMvc;

    protected static final String TestFileDir = "src/test/files/";

    @Before
    public void setUp() throws Exception {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
                .addFilter(openEntityManagerInViewFilter, "/*")
                .addFilter(hiddenHttpMethodFilter, "/*")
                .addFilter(characterEncodingFilter, "/*")
                .addFilter(sessionFilter, "/*")
                .addFilter(workflowAsSessionFilter, "/*")
                .addFilter(springSecurityFilterChain, "/*")
                .addFilter(requestFilter, "/*")
                .build();

        servletContext.setContextPath("/");

        Session session = Session.findBySessionId(httpSession.getId());
        if (session == null) {
            session = new Session();
            session.setJsessionid(httpSession.getId());
            session.persist();
        }
    }
...

问题在于,当我尝试对此进行调试时,perform对象上的 mockMvc 操作永远不会触及我的控制器方法。我认为这是一个通过我们的安全过滤器的问题(这就是我拥有所有login和东西的原因)但是我在词汇表控制器中测试了其他动作,我能够很好地击中它们。

思考?想法?建议?

2 个答案:

答案 0 :(得分:3)

好的,发现了这个问题。

Spring MockMultipartHttpServletRequestBuilder最终会返回MockHttpMultipartServletRequest个对象。

浏览器的作用是发布一个多部分编码的请求,然后由XML中定义的CommonsMultipartResolver bean拾取和解析。

然而,在测试中,由于我们已经发布了MockHttpMultipartServletRequest,我们不希望解析器解析这个,所以我们要做的就是有一个解析器没有启动的配置文件。 / p>

然而,我们选择做的是最终构建一个具有多部分编码的MockHttpServletRequest并将其放入Spring过滤器中,以便我们也可以集成测试旋转变压器。

不幸的是,我没有在Spring测试库中看到任何支持/帮助器,它允许你对它进行MockHttpServletRequest addPart() ,或者是那种效果=&GT;手动编码的浏览器仿真功能:(

答案 1 :(得分:2)

如何测试分段上传的简单方法是使用StandardServletMultipartResolver。 并且为了测试使用此代码:

    final MockPart profilePicture = new MockPart("profilePicture", "stview.jpg", "image/gif", "dsdsdsd".getBytes());
    final MockPart userData = new MockPart("userData", "userData", "application/json", "{\"name\":\"test aida\"}".getBytes());

    this.mockMvc.perform(
            fileUpload("/endUsers/" + usr.getId().toString()).with(new RequestPostProcessor() {

                @Override
                public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
                    request.addPart(profilePicture);
                    request.addPart(userData);
                    return request;
                }
            }) 

MockPart类

public class MockPart extends MockMultipartFile implements Part {

private Map<String, String> headers;

public MockPart(String name, byte[] content) {
    super(name, content);
    init();
}

public MockPart(String name, InputStream contentStream) throws IOException {
    super(name, contentStream);
    init();
}

public MockPart(String name, String originalFilename, String contentType, byte[] content) {
    super(name, originalFilename, contentType, content);
    init();
}

public MockPart(String name, String originalFilename, String contentType, InputStream contentStream) throws IOException {
    super(name, originalFilename, contentType, contentStream);
    init();
}

public void init() {
    this.headers = new HashMap<String, String>();
    if (getOriginalFilename() != null) {
        this.headers.put("Content-Disposition".toLowerCase(), "form-data; name=\"" + getName() + "\"; filename=\"" + getOriginalFilename() + "\"");
    } else {
        this.headers.put("Content-Disposition".toLowerCase(), "form-data; name=\"" + getName() + "\"");
    }
    if (getContentType() != null) {
        this.headers.put("Content-Type".toLowerCase(), getContentType());
    }
}

@Override
public void write(String fileName) throws IOException {
}

@Override
public void delete() throws IOException {
}

@Override
public String getHeader(String name) {
    return this.headers.get(name.toLowerCase());
}

@Override
public Collection<String> getHeaders(String name) {
    List<String> res = new ArrayList<String>();
    if (getHeader(name) != null) {
        res.add(getHeader(name));
    }
    return res;
}

@Override
public Collection<String> getHeaderNames() {
    return this.headers.keySet();
}

}