import com.guazi.config.Constants;
import com.guazi.utils.GetSignatureUtils;
import junit.framework.TestCase;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Map;
/**
* @author wangchen
* @version 1.0
* @date 2021/12/14 2:44 下午 @Desc Filter 过滤器验证请求 /test/junit 接口的请求参数 signature
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class SsoFilterTest extends TestCase {
@Autowired private GetSignatureUtils getSignatureUtils;
private SsoFilter filter = new SsoFilter();
private String failed = "{\"msg\":\"check failed.\",\"code\":\"5002\",\"data\":\"\"}";
@Test
public void testDoFilter() {
//通过getSignature() 获取验签参数
String rightSignature=getSignature();
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
MockFilterChain filterChain = new MockFilterChain();
req.setRequestURI("/test/junit");
req.setParameter("name", "test_name");
req.setParameter("signature", rightSignature);
try {
filter.doFilter(req, res, filterChain);
Assert.assertEquals(res.getStatus(), HttpStatus.OK.value());
Assert.assertEquals(failed, res.getContentAsString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里由于容器加载顺序(加载顺序:Listener–>Filter–>Servlet),当项目启动时,Filter 初始化先于 Servlet, 而 Spring 中 Bean初始化是在 Servlet后,导致在 Filter 中无法注入。我 Filter 这里需要使用到一个获取 Singnature 的工具类,我们都知道此时 @Autowired
无效的,所以我把这个操作放到了 Filter 的 init() 中,去初始化我所需要的 Bean:
private static SignatureUtils signatureUtils;
@Override
public void init(FilterConfig filterConfig) {
ServletContext context = filterConfig.getServletContext();
try {
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
if (ctx != null) {
signatureUtils = ctx.getBean(SignatureUtils.class);
}
} catch (Exception e) {
logger.error("SsoFilter Exception: {}", e);
}
}
添加单元测试类时,起初我使用的是@SpringBootTest(classes = Main.class)
,这里就坑了我很长时间,filter.doFilter()
直接会报空指针异常,排查发现问题是我手动调用了其 init() 做初始化,但ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
这里我们 get 到的是 null。
直接说结论:
需要将上述注解改为
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
。 SpringBoot 单元测试没有对 Filter
进行初始化,但是 Filter 在请求过程中被执行了,因此抛出了空指针异常。我们可以指定 webEnvironment 属性,默认是
WebEnvironment.MOCK,它是不会对Filter、Servlet进行初始化的,Spring 为我们提供了
WebEnvironment.RANDOM_PORT、WebEnvironment.DEFINED_PORT,可以自动为我们初始化Filter、Servlet。