Filter 过滤器单元测试之空指针异常

翟冯浩
2023-12-01
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。

 类似资料: