Skip to main content
 首页 » 编程设计

spring-mvc之让mockMVC 测试过滤器的init 例程需要什么

2024年08月15日7kenshinobiy

我已经实现了以下 CORS 过滤器,该过滤器在服务器上执行代码时起作用:

/* 
 *    Copyright 2013 BrandsEye (http://www.brandseye.com) 
 * 
 *    Licensed under the Apache License, Version 2.0 (the "License"); 
 *    you may not use this file except in compliance with the License. 
 *    You may obtain a copy of the License at 
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0 
 * 
 *    Unless required by applicable law or agreed to in writing, software 
 *    distributed under the License is distributed on an "AS IS" BASIS, 
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 *    See the License for the specific language governing permissions and 
 *    limitations under the License. 
 */ 
 
package org.energyos.espi.datacustodian.web.filter; 
 
import javax.servlet.*; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 
import java.util.Enumeration; 
import java.util.LinkedHashMap; 
import java.util.Map; 
import java.util.regex.Pattern; 
 
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
 
import org.springframework.stereotype.Component; 
 
/** 
 * Adds CORS headers to requests to enable cross-domain access. 
 */ 
 
@Component 
public class CORSFilter implements Filter { 
 
    private final Log logger = LogFactory.getLog(getClass()); 
    private final Map<String, String> optionsHeaders = new LinkedHashMap<String, String>(); 
 
    private Pattern allowOriginRegex; 
    private String allowOrigin; 
    private String exposeHeaders; 
 
    public void init(FilterConfig cfg) throws ServletException { 
        String regex = cfg.getInitParameter("allow.origin.regex"); 
        if (regex != null) { 
            allowOriginRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); 
        } else { 
            optionsHeaders.put("Access-Control-Allow-Origin", "*"); 
        } 
 
        optionsHeaders.put("Access-Control-Allow-Headers", "Origin, Authorization, Accept, Content-Type"); 
        optionsHeaders.put("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); 
        optionsHeaders.put("Access-Control-Max-Age", "1800"); 
        for (Enumeration<String> i = cfg.getInitParameterNames(); i.hasMoreElements(); ) { 
            String name = i.nextElement(); 
            if (name.startsWith("header:")) { 
                optionsHeaders.put(name.substring(7), cfg.getInitParameter(name)); 
            } 
        } 
 
        //maintained for backward compatibility on how to set allowOrigin if not 
        //using a regex 
        allowOrigin = optionsHeaders.get("Access-Control-Allow-Origin"); 
        //since all methods now go through checkOrigin() to apply the Access-Control-Allow-Origin 
        //header, and that header should have a single value of the requesting Origin since 
        //Access-Control-Allow-Credentials is always true, we remove it from the options headers 
        optionsHeaders.remove("Access-Control-Allow-Origin"); 
 
        exposeHeaders = cfg.getInitParameter("expose.headers"); 
    } 
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 
            throws IOException, ServletException { 
 
        if (logger.isDebugEnabled()) {           
            logger.debug("CORSFilter processing: Checking for Cross Origin pre-flight OPTIONS message"); 
        } 
 
        if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { 
            HttpServletRequest req = (HttpServletRequest)request; 
            HttpServletResponse resp = (HttpServletResponse)response; 
            if ("OPTIONS".equals(req.getMethod())) { 
                allowOrigin = "*";                                          //%%%%% Test force of allowOrigin 
                if (checkOrigin(req, resp)) { 
                    for (Map.Entry<String, String> e : optionsHeaders.entrySet()) { 
                        resp.addHeader(e.getKey(), e.getValue()); 
                    } 
 
                    // We need to return here since we don't want the chain to further process 
                    // a preflight request since this can lead to unexpected processing of the preflighted 
                    // request or a 40x - Response Code 
                    return; 
 
                } 
            } else if (checkOrigin(req, resp)) { 
                if (exposeHeaders != null) { 
                    resp.addHeader("Access-Control-Expose-Headers", exposeHeaders); 
                } 
            } 
        } 
        filterChain.doFilter(request, response); 
    } 
 
    private boolean checkOrigin(HttpServletRequest req, HttpServletResponse resp) { 
        String origin = req.getHeader("Origin"); 
        if (origin == null) { 
            //no origin; per W3C specification, terminate further processing for both pre-flight and actual requests 
            return false; 
        } 
 
        boolean matches = false; 
        //check if using regex to match origin 
        if (allowOriginRegex != null) { 
            matches = allowOriginRegex.matcher(origin).matches(); 
        } else if (allowOrigin != null) { 
            matches = allowOrigin.equals("*") || allowOrigin.equals(origin); 
        } 
 
        if (matches) { 
 
            // Activate next two lines and comment out third line if Credential Support is required 
//          resp.addHeader("Access-Control-Allow-Origin", origin); 
//          resp.addHeader("Access-Control-Allow-Credentials", "true");          
            resp.addHeader("Access-Control-Allow-Origin", "*"); 
            return true; 
        } else { 
            return false; 
        } 
    } 
 
    public void destroy() { 
    } 
} 

以下 JUnit 测试使用 mockMVC 但失败,因为 CORSFilter 的“init”逻辑未执行(通过在 JUnit 测试中设置断点来证明):

package org.energyos.espi.datacustodian.integration.web.filters; 
 
 
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
 
import javax.servlet.FilterConfig; 
 
import org.energyos.espi.datacustodian.web.filter.CORSFilter; 
 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Profile; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 
import org.springframework.test.context.web.WebAppConfiguration; 
import org.springframework.test.web.servlet.MockMvc; 
import org.springframework.test.web.servlet.MvcResult; 
import org.springframework.test.web.servlet.RequestBuilder; 
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 
import org.springframework.web.context.WebApplicationContext; 
 
import static org.hamcrest.Matchers.is; 
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; 
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; 
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 
 
@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration("/spring/test-context.xml") 
@Profile("test") 
public class CORSFilterTests { 
 
    private final Log logger = LogFactory.getLog(getClass());    
 
    @Autowired 
    private CORSFilter filter; 
 
    @Autowired 
    private WebApplicationContext wac; 
 
    private MockMvc mockMvc; 
 
    @Before 
    public void setup() { 
        this.mockMvc = webAppContextSetup(this.wac) 
                .addFilters(filter).build(); 
    } 
 
    @Test 
    public void optionsResponse_hasCorrectFilters() throws Exception { 
 
        RequestBuilder requestBuilder = MockMvcRequestBuilders.options("/DataCustodian/oauth/token") 
                .header("Origin", "foobar") 
                .header("Access-Control-Allow-Origin", "*");  
 
        MvcResult result =   mockMvc.perform(requestBuilder) 
                .andExpect(header().string("Access-Control-Allow-Origin", is("*"))) 
                .andExpect(header().string("Access-Control-Allow-Methods", is("GET, POST, PUT, DELETE, OPTIONS"))) 
                .andExpect(header().string("Access-Control-Allow-Headers", is("origin, authorization, accept, content-type"))) 
                .andExpect(header().string("Access-Control-Max-Age", is("1800")))                
                .andReturn();       
        } 
    } 
} 

我已经查看了互联网上的可用 Material ,这似乎暗示了“.addfilter(filter)。mockMVC @Before 部分的元素应该执行 CORSFilter init 例程。但是,这显然没有发生。

任何建议或建议将不胜感激,因为我真的很难理解如何使用mockMVC功能测试“init”例程。

请您参考如下方法:

Spring MVC 测试套件并不是为了测试容器配置,而是为了测试您的 MVC(@Controller 和其他映射)配置。 Filter#init(ServletConfig) 是容器管理的方法。

如果你确实需要测试它,你也可以模拟它

@Before 
public void setup() { 
    filter.init(someMockFilterConfig); // using a mock that you construct with init params and all 
    this.mockMvc = webAppContextSetup(this.wac) 
            .addFilters(filter).build(); 
}