测试拦截器(通常是指如 Spring MVC 的 HandlerInterceptor、MyBatis 的 Interceptor、OKHttp/Retrofit 的 Interceptor 等)需要根据具体技术栈来设计测试方案。不过,核心思路是一致的:验证在特定条件下,拦截器的逻辑是否被正确执行。
下面我将以使用最广泛的 Spring MVC 的 HandlerInterceptor 为例,详细讲解如何测试,并补充其他常见场景的测试思路。
假设我们有一个简单的拦截器,用于检查请求头中是否存在 API-Key。
示例代码:
@Component
public class ApiKeyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String apiKey = request.getHeader("API-Key");
if (apiKey == null || apiKey.isEmpty()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("Missing API Key");
return false; // 中断请求
}
return true; // 放行
}
}
这是最合适的方法,因为它模拟了 Spring MVC 的完整流程,但又不需要启动真正的服务器(速度快)。
测试代码:
@WebMvcTest(YourController.class) // 或者 @SpringBootTest
class ApiKeyInterceptorTest {
@Autowired
private MockMvc mockMvc;
// 如果拦截器不是自动装配的,需要手动添加
// @Autowired
// private ApiKeyInterceptor apiKeyInterceptor;
// 或者这样配置
// @BeforeEach
// void setup(WebApplicationContext wac) {
// this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).addInterceptors(apiKeyInterceptor).build();
// }
@Test
@DisplayName("当缺少 API-Key 时应返回 401")
void shouldReturnUnauthorizedWhenApiKeyMissing() throws Exception {
mockMvc.perform(get("/some-endpoint") // 发送请求
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isUnauthorized());
}
@Test
@DisplayName("当 API-Key 存在时应返回 200")
void shouldReturnOkWhenApiKeyPresent() throws Exception {
mockMvc.perform(get("/some-endpoint")
.header("API-Key", "valid-key-123"))
.andExpect(status().isOk());
}
}
注意: 如果拦截器是通过 WebMvcConfigurer 配置的,且你在测试类上使用了 @WebMvcTest,Spring 应该会自动检测到它。如果没有,你可能需要在测试配置中手动注册拦截器。
如果你想测试真实的 HTTP 请求流程(包括 Tomcat 启动),可以使用这种方式。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiKeyInterceptorIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void testInterceptorViaRealRequest() {
// 不带 Header
ResponseEntity<String> response1 = restTemplate.getForEntity("/some-endpoint", String.class);
assert response1.getStatusCode().value() == 401;
// 带 Header
HttpHeaders headers = new HttpHeaders();
headers.set("API-Key", "test");
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response2 = restTemplate.exchange("/some-endpoint", HttpMethod.GET, entity, String.class);
assert response2.getStatusCode().is2xxSuccessful();
}
}
如果你只想测试拦截器本身的逻辑,不关心 Spring 的调度,可以直接 Mock HttpServletRequest 和 HttpServletResponse。
@ExtendWith(MockitoExtension.class)
class ApiKeyInterceptorUnitTest {
private ApiKeyInterceptor interceptor;
private HttpServletRequest request;
private HttpServletResponse response;
@BeforeEach
void setUp() {
interceptor = new ApiKeyInterceptor();
request = mock(HttpServletRequest.class);
response = mock(HttpServletResponse.class);
}
@Test
void preHandle_shouldReturnFalse_whenApiKeyIsMissing() throws Exception {
// Given
when(request.getHeader("API-Key")).thenReturn(null);
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(writer);
// When
boolean result = interceptor.preHandle(request, response, new Object());
// Then
assertFalse(result);
verify(response).setStatus(HttpStatus.UNAUTHORIZED.value());
}
@Test
void preHandle_shouldReturnTrue_whenApiKeyIsPresent() throws Exception {
// Given
when(request.getHeader("API-Key")).thenReturn("abc");
// When
boolean result = interceptor.preHandle(request, response, new Object());
// Then
assertTrue(result);
}
}
MyBatis 的拦截器主要用于 SQL 重写、分页等。测试起来稍微复杂,通常需要:
Configuration 对象。SqlSessionFactory 构建会话并执行方法,验证 SQL 是否被修改。示例思路:
@Test
void testMyBatisInterceptor() {
// 1. 创建配置
Configuration configuration = new Configuration();
Environment environment = new Environment("test", new JdbcTransactionFactory(), mock(DataSource.class));
configuration.setEnvironment(environment);
// 2. 添加拦截器
MyCustomInterceptor interceptor = new MyCustomInterceptor();
configuration.addInterceptor(interceptor);
// 3. 添加 Mapper 并执行
configuration.addMapper(UserMapper.class);
try (SqlSession session = new SqlSessionFactoryBuilder().build(configuration).openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectById(1); // 这里会触发拦截器
// 验证拦截器内部的逻辑是否执行(例如通过 spy 或静态标记)
}
}
对于网络库中的拦截器,测试通常是通过MockWebServer配合测试。
示例思路:
@Test
void testOkHttpInterceptor() throws IOException {
MockWebServer server = new MockWebServer();
server.start();
server.enqueue(new MockResponse().setBody("{\"status\":\"ok\"}"));
// 1. 添加拦截器到 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor()) // 你的拦截器
.build();
// 2. 发起请求
Request request = new Request.Builder()
.url(server.url("/test"))
.build();
Response response = client.newCall(request).execute();
// 3. 验证
assertEquals(200, response.code());
// 验证拦截器是否添加了 Header 等
RecordedRequest recordedRequest = server.takeRequest();
assertEquals("expected_header_value", recordedRequest.getHeader("Custom-Header"));
server.shutdown();
}
preHandle(请求前)、postHandle(视图渲染前)还是 afterCompletion(完成后)。MockHttpServletRequest 或 MockMvc。Configuration。MockWebServer。HttpServletRequest、DataSource 或 Chain 对象。true 或 false)。response.setStatus(401) 是否被调用)。建议: 对于 Spring 项目,优先使用 MockMvc 测试拦截器,因为它最接近真实使用情况且速度较快。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。