Java 测试Spring MultipartTTPServletRequest

Java 测试Spring MultipartTTPServletRequest,java,spring,spring-mvc,Java,Spring,Spring Mvc,尝试测试一个spring控制器,我们有多个文件上传。这是控制器: @RequestMapping("/vocabularys") @Controller public class VocabularyController { ... 我要测试的操作: @RequestMapping(value = "/import", method = {RequestMethod.PUT, RequestMethod.POST}) @ResponseBody @CacheEvict(value="vocab

尝试测试一个spring控制器,我们有多个文件上传。这是控制器:

@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"/>
忽略
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();
        }
    }
...
问题是,当我尝试调试它时,对
mockMvc
对象执行的
perform
操作从未命中我的控制器方法。我认为这是一个通过我们的安全过滤器的问题(这就是为什么我有所有的
登录
等等),但我在词汇控制器中测试了其他操作,我能够很好地命中它们


想法?思想?建议?

好的,找到了问题

Spring的
MockMultipartTtpServletRequestBuilder
最终返回一个
MockHttpMultipartServletRequest
对象

然而,浏览器所做的是发布一个多部分编码的请求,然后由XML中定义的
commonmultipartresolver
bean提取并解析该请求

然而,在测试中,由于我们已经发布了一个
MockHttpMultipartServletRequest
,我们不希望解析器解析它,所以我们所要做的就是拥有一个解析程序不启动的概要文件

然而,我们选择做的是构建一个具有多部分编码的
MockHttpServletRequest
,并将其放入Spring过滤器,以便我们也可以集成测试解析器


不幸的是,我在Spring测试库中没有看到任何支持/帮助程序,它允许您将
MockHttpServletRequest
addPart()
添加到它,或者类似的东西=>手工编码的浏览器模拟函数:(

测试多部分上载的简单方法是使用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;
                }
            }) 
模拟零件类

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();
}
公共类MockPart扩展MockMultipartFile实现部分{
私有映射头;
公共MockPart(字符串名称,字节[]内容){
超级(名称、内容);
init();
}
公共MockPart(字符串名称,InputStream contentStream)引发IOException{
超级(名称、内容流);
init();
}
公共MockPart(字符串名称、字符串原始文件名、字符串内容类型、字节[]内容){
super(名称、原始文件名、内容类型、内容);
init();
}
公共MockPart(字符串名称、字符串原始文件名、字符串contentType、InputStream contentStream)引发IOException{
super(名称、原始文件名、contentType、contentStream);
init();
}
公共void init(){
this.headers=new HashMap();
如果(getOriginalFilename()!=null){
this.headers.put(“Content Disposition.toLowerCase(),”表单数据;name=\”“+getName()+“\”;filename=\”“+getOriginalFilename()+“\”);
}否则{
this.headers.put(“Content Disposition.toLowerCase(),”表单数据;name=\“”+getName()+“\”);
}
如果(getContentType()!=null){
this.headers.put(“Content-Type.toLowerCase(),getContentType());
}
}
@凌驾
公共无效写入(字符串文件名)引发IOException{
}
@凌驾
public void delete()引发IOException{
}
@凌驾
公共字符串getHeader(字符串名称){
返回this.headers.get(name.toLowerCase());
}
@凌驾
公共集合getHeaders(字符串名称){
List res=new ArrayList();
if(getHeader(name)!=null){
res.add(getHeader(name));
}
返回res;
}
@凌驾
公共集合总部名称(){
返回此.headers.keySet();
}

}

确切地说:没有“允许您接受MockHttpServletRequest和addPart()”:-(在当前版本中,有
MockMvcRequestBuilders.fileUpload
为什么
“内容配置”
上的
.toLowerCase
?如何安装
标准ServletMultipartResolver
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();
}