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

标签 spring-mvc junit mocking servlet-filters

我已经实现了以下 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();
}

关于spring-mvc - 让mockMVC 测试过滤器的init 例程需要什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20819130/

相关文章:

java - Spring 4 + Hibernate 4 配置

java - 同一型号不等于

unit-testing - 如何在使用 VirtualPathUtility.GetAbsolute 方法对代码进行单元测试时模拟上下文

c# - 设置字符串参数时,模拟上的所有调用都必须有相应的设置

ruby-on-rails - 涉及需要 api 更新的对象的 Rails 测试

java - 为什么Spring(4) mvc :default-servlet-handler overrides controller mapping?

java - Spring beans - 验证是否正确初始化

java - Spring - 如何从 Web 应用程序停止和启动 Web 服务?

android - 模拟器上的 Gradle JUnit Espresso connectedAndroidTest java.lang.IncompatibleClassChangeError

java - Gradle junit编码问题