【问题标题】:Spring Boot test FilterSpring Boot 测试过滤器
【发布时间】:2021-08-28 12:41:11
【问题描述】:

注意:下面的错误实际上是由过滤器中的拼写错误引起的,请参阅@jccampanero 在下面的回答中提到

我一直在尝试几种不同的方法来测试过滤器,但是我不断收到一个或另一个错误,所以我希望有一些方向。

这是一个虚拟过滤器,应该只进行重定向。


package org.example.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class RedirectFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) req;
    chain.sendRedirect("/splash");
  }
}

还有一个基本的 Spring Boot Application 类:

package org.example;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApplicationClass {

}

我想我只是不清楚我正在尝试做的 Spring Boot 测试的“级别”,这是我做过的一些尝试和错误:

选项 1. 尝试使用模拟,例如来自 here

package org.example.filters;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.junit.jupiter.api.Test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

public class LocaleFilterIntegrationTestStandalone {

  @Test
  public void whenNoLocaleRedirectToSplash() throws Exception {
    standaloneSetup(new TestController()).addFilters(new RedirectFilter()).build().perform(get("/"))
        .andExpect(status().isFound()).andExpect(redirectedUrl("/splash"));
  }

  @Controller
  private static class TestController {
    @GetMapping("/")
    public String get() {
      return "got it";
    }
  }
}

错误:java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse

选项2,尝试使用@WebMvcTest,与选项1有同样的问题

package org.example.filters;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;

@WebMvcTest
public class LocaleFilterIntegrationTestWebMvc {
  @Autowired
  private MockMvc mvc;

  @Test
  public void noLanguageShouldRedirectToSplash() throws Exception {
    mvc.perform(get("/")).andExpect(status().isFound()).andExpect(redirectedUrl("/splash"));
  }

  @Controller
  private static class TestController {
    @GetMapping("/")
    public String get() {
      return "got it";
    }
  }
}

错误:java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse

选项 3,尝试启动整个上下文,我认为这是能够转换为 HttpServletRequest 所必需的:

package org.example.filters;

import java.net.URI;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class LocaleFilterIntegrationTest {
  @LocalServerPort
  private int port;

  @Autowired
  private TestRestTemplate restTemplate;

  @Test
  public void noLanguageShouldRedirectToSplash() throws Exception {
    URI uri = new URI("http", "localhost:" + port, "/", null, null);
    String result = restTemplate.getForObject(uri.toString(), String.class);
    // not sure how, but test for redirect...
  }

  @Controller
  private static class TestController {
    @GetMapping("/")
    public String get() {
      return "got it";
    }
  }
}

错误:class org.apache.catalina.connector.RequestFacade cannot be cast to class javax.servlet.http.HttpServletResponse

@m-deinem 建议的选项 4

package org.example.filters;

import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

public class LocaleFilterIntegrationTestPlain {
  private final RedirectFilter redirectFilter = new RedirectFilter();

  @Test
  public void noLanguageShouldRedirectToSplash() throws Exception {
    MockHttpServletRequest req = new MockHttpServletRequest();
    MockHttpServletResponse res = new MockHttpServletResponse();
    MockFilterChain chain = new MockFilterChain();
    redirectFilter.doFilter(req, res, chain);
  }
}

错误:java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse

【问题讨论】:

  • 你不能在FilterChain 上执行sendRedirect ...也许你可以使用GenericFilterBean 而不是普通的servlet Filter
  • @DirkDeyne 所以您的建议是,例如,实施OncePerRequestFilter 而不仅仅是Filter。还有其他方法可以在测试中获得真正的HttpServletRequest 吗?
  • 只需创建一个过滤器实例,使用模拟类并检查结果。您为此过滤器的所有测试都过于复杂。

标签: java spring spring-boot spring-mvc spring-test


【解决方案1】:

首先您的RedirectFilter 包含一个错误,它尝试将传入的HttpServletRequest 转换为HttpServletResponse,因为您使用了错误的变量。

@Component
public class RedirectFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    res.sendRedirect("/splash");
  }
}

两行都使用req,而后者应该使用res(这也是异常字面意思告诉你的)。

接下来只写一个简单的单元测试,尝试编写集成测试会让事情变得过于复杂。

public class RedirectFilterTest

  private final RedirectFilter filter = new RedirectFilter();

  @Test
  public void redirectTest() {
    MockHttpServletRequest req = new MockHttpServletRequest();
    MockHttpServletResponse res = new MockHttpServletResponse();
    MockFilterChain chain = new MockFilterChain();
 
    filter.doFilter(req, res, chain);

    assertEquals("/splash", res.getRedirectedUrl());
  }

【讨论】:

  • 这似乎不起作用,我仍然得到java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse
  • 非常好的实现@M。 Deinum
  • 当然不是,您不能将请求转换为响应,这是您自己的代码中的错误。 HttpServletResponse response = (HttpServletResponse) req; 那行是错误的,因为它将请求转换为响应。您草率地粘贴了上一行并更改了强制转换而不是要使用的参数(使用res 而不是req)。
  • 我修改了答案以包含这个。
【解决方案2】:

拜托,也许这只是一个错字,但请注意,在您的 RedirectFilter doFilter 方法中,您错误地将变量 req 转换为 HttpServletResponse

package org.example.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class RedirectFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    // Please, note the incorrect cast
    HttpServletResponse response = (HttpServletResponse) req;
    chain.sendRedirect("/splash");
  }
}

相反,它应该是:

package org.example.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class RedirectFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    chain.sendRedirect("/splash");
  }
}

这很可能是ClassCastException 和测试失败的原因。

一旦解决,@M 提出的解决方案。 Deinum 是一个非常好的和直接的。

【讨论】:

  • Wooooow,是的,就是这样。嗯,谢谢你这么有鹰眼!
  • 欢迎您@errorline1。很高兴看到您解决了问题。
猜你喜欢
  • 2015-05-22
  • 1970-01-01
  • 2021-10-19
  • 2017-06-09
  • 1970-01-01
  • 2017-02-25
  • 2018-01-02
  • 2021-11-04
  • 2020-10-30
相关资源
最近更新 更多