2013-10-02 34 views
2

嘗試測試我們用於多個文件上傳的彈簧控制器。這裏是控制器:測試Spring MultipartHttpServletRequest

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

的問題是,當我嘗試調試這一點,mockMvc對象的perform行動從來沒有擊中我的控制器方法。我認爲這是一個通過我們的安全過濾器的問題(這就是爲什麼我有所有login和東西),但我測試了詞彙控制器中的其他操作,並且我能夠很好地擊中它們。

想法?想法?建議?

回答

3

好的,找到了問題。

Spring的MockMultipartHttpServletRequestBuilder最終返回一個MockHttpMultipartServletRequest對象。

然而,瀏覽器做的是發佈一個多部分編碼的請求,然後被XML中定義的CommonsMultipartResolver bean拾取並解析。

然而,在測試中,因爲我們已經張貼MockHttpMultipartServletRequest,我們不希望解析器解析這一點,所以我們要做的就是有一個輪廓,其中解析器不踢。

然而,我們選擇做的是最終構建一個MockHttpServletRequest,它具有多部分編碼,並通過彈簧過濾器,以便我們也可以集成測試解析器踢入。

不幸的是,我沒有看到任何支持/幫手在Spring的測試庫中,它可以讓你拍一個MockHttpServletRequestaddPart()或者其他東西=>手寫瀏覽器仿真功能:(

+0

正是:沒有什麼 「讓你採取MockHttpServletRequest和addPart()」 :-( – Snekse

+0

在當前版本,有'MockMvcRequestBuilders.fileUpload' – 2017-01-18 15:05:35

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

}

+0

爲什麼在''Content-Disposition''上的'.toLowerCase'' – Snekse

+0

'StandardServletMultipartResolver'它在? – Snekse