测试日志输出 Java Spring

Dav*_*vid 5 java junit logback slf4j spring-boot-test

我有一个在 java 10 上运行的 spring boot 2 应用程序,使用 SLF4J 和 logback 作为底层记录器。

给定以下弹簧组件:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class MyClass {

    private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);

    public void hello() {
        // more logic...
        LOG.trace("World");
    }
}
Run Code Online (Sandbox Code Playgroud)

以及希望验证日志输出(想象一个更复杂的场景,其中日志将包含变量和上下文信息)或生成日志消息的事实被认为是关键的测试。

import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;

import static org.hamcrest.Matchers.containsString;

public class MyClassTest {

    private final MyClass myClass = new MyClass();

    @Rule
    public final OutputCapture outputCapture = new OutputCapture();

    @Test
    public void successShouldLogSuccessMessages() {
        myClass.hello();
        outputCapture.expect(containsString("World"));
    }
}
Run Code Online (Sandbox Code Playgroud)

要通过此测试,必须将日志级别设置为TRACE。我注意到一些不可预测的行为,其中测试将失败或通过取决于测试运行的顺序 - 请参阅引入 WebFluxTest 时使用 Maven 和 Spring Boot 2 看似随机测试失败

我探索了一些选择,但没有一个看起来是“理想的”,但确实有效。

  1. 在测试类路径上提供一个 log back 配置文件,该文件将日志级别设置为至少跟踪这一类。
  2. 将此类的日志级别设置TRACE为 spring 应用程序属性类,并在 spring 上下文中运行测试。
  3. 在测试类中创建设置和拆除,在测试执行期间以编程方式修改日志级别,然后在之后重置它。

我目前的解决方案是让 Spring 完成配置记录器的工作并使测试成为 spring 上下文测试(并确保它快速运行只加载我关心的一个 bean),如下所示:

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
@ContextConfiguration(classes = MyClass.class)
public class MyClassTest {

    private final MyClass myClass = new MyClass();

    @Rule
    public final OutputCapture outputCapture = new OutputCapture();

    @Test
    public void successShouldLogSuccessMessages() {
        myClass.hello();
        outputCapture.expect(containsString("World"));
    }
}
Run Code Online (Sandbox Code Playgroud)

其中的内容application-test.properties是:

logging.level.com.example.MyClass=TRACE
Run Code Online (Sandbox Code Playgroud)

此解决方案有效,它不需要我以编程方式使用记录器或将 logback 配置放在类路径上,这使所有内容与底层日志框架完全无关,并使 logback 配置远离测试类路径也意味着其他测试关心日志记录并没有选择配置。

也就是说,感觉“很奇怪”,我从来没有真正见过有人以这种方式使用 spring 测试(测试一个没有依赖关系且不直接依赖上下文的 bean)。测试开始时是一个计划 POJO 测试,不关心春天,它保持这种方式感觉更“纯粹”。

在尝试通过 SLF4J 测试对象的日志输出时,是否有适用的最佳实践?

小智 6

您的方法是测试当前日志记录配置记录的内容,而不是测试发送到日志记录系统的内容。

您可以通过编写自己的附加程序或模拟 SLF4J 实现来拦截日志记录,而不是使用 OutputCapture 规则。如果记录了不正确的内容(例如 error ),您甚至可能会在自定义附加程序中使测试失败。我建议您创建自己的@Rule,添加您自己的测试附加程序,然后它可以执行您想要检查匹配器或确保记录正确数据的任何自定义逻辑。