1. 概述

代码执行时我们常记录关键事件日志。某些事件至关重要,需要确保特定消息被正确记录。本文将演示如何在 Spock测试 中验证类是否输出了指定日志消息。我们将使用 Logback(Spring 默认日志框架)的 ListAppender 捕获消息供后续校验。

2. 准备工作

首先创建一个包含日志输出的 ClassWithLogger 类,使用 @Slf4j 注解初始化日志对象,并添加一个记录 INFO 级别消息的方法:

@Slf4j
public class ClassWithLogger {
    void logInfo() {
        log.info("info message");
    }
}

3. 测试框架搭建

创建 ClassWithLoggerTest 测试类,并初始化被测对象:

class ClassWithLoggerTest extends Specification {
    @Subject
    def subject = new ClassWithLogger()
}

接下来配置日志监听器。从 LoggerFactory 获取日志对象并转换为 Logback 的 Logger 实例:

def logger = (Logger) LoggerFactory.getLogger(ClassWithLogger)

关键步骤:创建 ListAppender 收集日志消息,并将其添加到日志器中。使用 setup 方法确保每次测试前重置消息列表:

def setup() {
    listAppender = new ListAppender<>()
    listAppender.start()
    logger.addAppender(listAppender)
}

至此,我们已准备好捕获、收集并验证日志消息。

4. 验证简单消息

编写测试触发日志输出并校验消息。调用 logInfo() 方法后,验证 ListAppender 的消息列表中是否存在预期文本和日志级别:

def "当被测对象输出INFO日志时,验证日志内容"() {
    when: "调用输出INFO日志的方法"
    subject.logInfo()

    then: "校验捕获的消息"
    with(listAppender.list[0]) {
        getMessage() == expectedText
        getLevel() == Level.INFO
    }
}

⚠️ 此方法假设目标消息是第一条日志。当目标消息不确定位置时,需改用流式处理:

then: "通过流式处理查找目标消息"
def ourMessage = listAppender.list.stream()
  .filter(logEvent -> logEvent.getMessage().contains(expectedText))
  .findAny()
ourMessage.isPresent()

and: "校验消息详情"
with(ourMessage.get()) {
    getMessage() == expectedText
    getLevel() == Level.INFO
}

5. 验证格式化消息

当调用 LoggingEventgetMessage() 时,返回的是消息模板而非格式化字符串。对于带参数的日志消息,必须使用 getFormattedMessage() 获取实际输出内容

ClassWithLogger 添加带参数的日志方法:

void logInfoWithParameter(String extraData) { 
    log.info("info message: {}", extraData); 
}

编写测试调用新方法:

def "当被测对象输出带参数的INFO日志时,验证消息格式"() {
    when: "调用输出INFO日志的方法"
    subject.logInfoWithParameter("parameter")
}

校验第一条日志消息时,需检查模板参数和格式化后的完整消息:

then: '校验列表中第一条消息的详情'
with (listAppender.list[0]) {
    getMessage() == 'info message: {}'      // ✅ 返回模板
    getArgumentArray()[0] == 'parameter'   // ✅ 校验参数值
    getFormattedMessage() == 'info message: parameter' // ✅ 校验完整消息
    getLevel() == Level.INFO
}

核心要点:getMessage() 返回模板字符串,getFormattedMessage() 返回实际日志输出

6. 总结

本文介绍了使用 Logback 的 ListAppender 捕获日志消息的核心技巧,包括:

  • 通过 LoggingEvent 验证简单消息
  • 校验带参数的模板化消息及其参数值
  • 区分消息模板与实际输出内容的验证方法

这些技术能有效确保关键日志在测试场景下的正确输出,避免生产环境中日志缺失的隐患。


原始标题:How to Check for a Logged Message in a Spock Test | Baeldung