3

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

@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()andlogin()和东西 - 这些是为了通过一些安全过滤器。

的相关部分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东西的原因),但我在词汇控制器中测试了其他操作,我能够很好地击中它们。

想法?想法?建议?

4

2 回答 2

3

好吧,找到问题了。

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

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

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

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

不幸的是,我在 Spring 测试库中没有看到任何支持/帮助程序,它允许您使用它MockHttpServletRequestaddPart()或者其他类似的东西 => 手工编码的浏览器仿真功能 :(

于 2013-10-02T17:45:34.080 回答
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;
                }
            }) 

模拟零件类

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();
}

}

于 2014-07-09T13:48:35.930 回答