You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JUnit模拟HTTP请求头与请求体进行单元测试

LoggingMethods 类单元测试实现方案

核心测试逻辑

两个方法均无返回值,核心逻辑是拼接请求参数后输出INFO级别日志,单元测试不需要启动完整Spring容器,核心验证点为传入指定请求参数时,日志输出内容符合预期。

前置准备

需要用到的测试组件:

  • JUnit 5(Jupiter)作为测试运行框架
  • Mockito 用于Mock ServerHttpRequest 接口实例,无需构造真实HTTP请求对象
  • 基于项目自带的日志框架(默认Logback)实现内存日志捕获,无需额外引入第三方日志测试依赖

注意:LoggingMethods的构造方法、两个成员方法均为包访问权限,测试类需要放置在和LoggingMethods相同的包路径下(test目录的对应包中),否则无法访问被测方法。

实现步骤

  1. Mock请求对象构造
    利用Mockito直接Mock ServerHttpRequest 接口,根据测试场景打桩返回对应值:
    • 测试appendBodyRequest时:Mock请求头对象,塞入自定义测试头键值对;将测试用的请求体字符串转为UTF-8编码的ByteBuffer实例
    • 测试logRequestNoBody时:打桩返回指定的HTTP请求方法枚举值(如GET/POST)
  2. 日志输出捕获
    在测试类初始化阶段,给LoggingMethods类对应的Logger绑定一个内存日志追加器,把测试过程中该类输出的所有日志暂存到内存集合中,测试执行完成后移除追加器避免影响其他测试用例。
  3. 断言验证
    调用被测方法后,读取内存中捕获的日志内容,断言日志包含预期拼接的参数值即可。

参考测试代码

package 替换为你项目中LoggingMethods所在的实际包名;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.ServerHttpRequest;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class LoggingMethodsTest {

    @Mock
    private ServerHttpRequest mockRequest;

    private ListAppender<ILoggingEvent> logAppender;
    private Logger classLogger;

    @BeforeEach
    void setUp() {
        // 初始化日志捕获器
        classLogger = (Logger) LoggerFactory.getLogger(LoggingMethods.class);
        logAppender = new ListAppender<>();
        logAppender.start();
        classLogger.addAppender(logAppender);
    }

    @AfterEach
    void tearDown() {
        // 移除日志追加器,避免污染其他测试
        classLogger.detachAppender(logAppender);
    }

    @Test
    void appendBodyRequest_shouldLogHeadersAndBody() {
        // 1. 构造测试参数
        // 构造测试请求头
        HttpHeaders testHeaders = new HttpHeaders();
        testHeaders.add("Content-Type", "application/json");
        testHeaders.add("X-Request-Id", "test-123456");
        when(mockRequest.getHeaders()).thenReturn(testHeaders);
        // 构造测试请求体ByteBuffer
        String testBody = "{\"userId\":1,\"userName\":\"test\"}";
        ByteBuffer testBodyBuffer = StandardCharsets.UTF_8.encode(testBody);

        // 2. 执行被测方法
        LoggingMethods loggingMethods = new LoggingMethods(mockRequest);
        loggingMethods.appendBodyrequest(testBodyBuffer, mockRequest);

        // 3. 验证日志输出
        assertEquals(1, logAppender.list.size());
        ILoggingEvent logEvent = logAppender.list.get(0);
        // 验证日志包含预期的头信息
        Map<String, String> loggedHeaders = (Map<String, String>) logEvent.getArgumentArray()[0];
        assertEquals("application/json", loggedHeaders.get("Content-Type"));
        assertEquals("test-123456", loggedHeaders.get("X-Request-Id"));
        // 验证日志包含预期的请求体
        String loggedBody = logEvent.getArgumentArray()[1].toString();
        assertEquals(testBody, loggedBody);
        assertEquals("Headers:[{}] , Body:[{}]", logEvent.getMessage());
    }

    @Test
    void logRequestNoBody_shouldLogMethodWithTip() {
        // 1. 构造测试参数
        when(mockRequest.getMethod()).thenReturn(HttpMethod.POST);

        // 2. 执行被测方法
        LoggingMethods loggingMethods = new LoggingMethods(mockRequest);
        loggingMethods.logRequestNoBody(mockRequest);

        // 3. 验证日志输出
        assertEquals(1, logAppender.list.size());
        ILoggingEvent logEvent = logAppender.list.get(0);
        assertEquals("{} response has no body.", logEvent.getMessage());
        assertEquals(HttpMethod.POST, logEvent.getArgumentArray()[0]);
    }
}

常见注意点

  • 如果你的项目用的不是Logback而是其他日志框架,替换日志捕获的实现即可,核心思路是把日志输出重定向到可读取的内存容器中做断言
  • Mock请求头时如果需要模拟多值头,直接调用HttpHeaders的addAll方法塞入多值列表即可,toSingleValueMap会自动取第一个值输出
  • 测试ByteBuffer时注意编码一致,必须和被测方法中使用的StandardCharsets.UTF_8保持一致,避免中文乱码导致断言失败

内容的提问来源于stack exchange,提问作者sk001

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 20:16:04