By taking TDD one step further, and specifying what the logging output should look like before writing the code we can expect that the logs will give maintainers clear and useful logs.
Based on Jaroslaw Michalik's article Don't mock static: test SLF4J Logger with appenders.
For JavaScript applications, refer to log-dd.
TestAppender
assumes that the application uses ch.qos.logback.classic.Logger
, which is the default provided by Spring Boot.
This library is available from Maven Central or Git Hub Packages
dependencies {
...
testImplementation 'io.github.nalbion:test-appender:1.0.3'
}
<dependencies>
...
<dependency>
<groupId>io.github.nalbion</groupId>
<artifactId>test-appender</artifactId>
<version>1.0.3</version>
<scope>test</scope>
</dependency>
</dependencies>
...
import io.github.nalbion.TestAppender;
import org.junit.jupiter.api.Test;
class MyTest {
private final TestAppender testAppender = new TestAppender(true);
@BeforeEach
void setup() {
testAppender.start();
}
@Test
void myTest() {
// When
new MyApplication().doSomething();
// Then
// Check all logs match a multi-line string
testAppender.assertLogs("""
Hello World!
My application calls log.info() twice.""");
// Check that only some lines are logged at WARN
testAppender.assertLogs(Level.WARN, "My application calls log.info() twice.");
// Check that at least one of the lines matches a Predicate
testAppender.assertAnyLogs(
TestAppender.atLogLevel(Level.INFO)
.and(e -> e.getFormattedMessage().matches("Hello .*!"))
);
testAppender.assertNoLogs(e -> e.getFormattedMessage().matches("Password: .*!"));
}
@Test
void dynamicValues() {
// When
logger.info("The time is {}", new SimpleDateFormat("HH:mm").format(new Date()));
// Then
// Handle dynamic values - date/time, random values
testAppender.assertLogs(line -> line.replaceFirst("\\b\\d{1,2}:\\d{2}\\b", "hh:mm"),
"The time is hh:mm");
}
}