
1. Introduction
As our code executes we often log events of interest. Some of those events are important enough that we want to ensure that our message is logged.
In this tutorial, we’ll learn how to verify that our class logged a specific message in a Spock Test. We’ll use a ListAppender from Logback, Spring’s default logging framework, to capture our messages for later inspection.
2. Setup
First, let’s create a ClassWithLogger Java class that logs some info and warning messages we want to verify. Let’s use an Slf4j annotation to instantiate the logger, and add a method that logs a message at an informational level:
@Slf4j
public class ClassWithLogger {
void logInfo() {
log.info("info message");
}
}
3. Our Test Framework
Now let’s create a ClassWithLoggerTest Specification with an instance of ClassWithLogger as our subject:
class ClassWithLoggerTest extends Specification {
@Subject
def subject = new ClassWithLogger()
}
Next, we must configure a Logger to listen to ClassWithLogger messages. So, let’s get a Logger from the LoggerFactory and cast it to a Logback Logger instance to use Logback’s features.
def logger = (Logger) LoggerFactory.getLogger(ClassWithLogger)
After that let’s create a ListAppender to collect our messages and add the appender to our Logger. Let’s use a setup method so that our collected messages are reset for each test:
def setup() {
listAppender = new ListAppender<>()
listAppender.start()
logger.addAppender(listAppender)
}
With our ListAppender set up, we’re ready to listen to log messages, collect them, and verify them in our test.
4. Checking for Simple Messages
Let’s write our first test to log a message and check for it. So, let’s log an INFO message by calling our ClassWithLogger‘s logInfo method and verify it’s in our ListAppender‘s message List with the expected text and log level:
def "when our subject logs an info message then we validate an info message was logged"() {
when: "we invoke a method that logs an info message"
subject.logInfo()
then: "we get our expected message"
with(listAppender.list[0]) {
getMessage() == expectedText
getLevel() == Level.INFO
}
}
In this case, we know our message was the first and only message logged so we can safely check for it at index 0 in the ListAppender’s list of captured messages.
When the log event we’re looking for isn’t necessarily the first message we’ll need a more robust approach. In this case, let’s stream the list of messages and filter it for our message:
then: "we get our expected message"
def ourMessage = listAppender.list.stream()
.filter(logEvent -> logEvent.getMessage().contains(expectedText))
.findAny()
ourMessage.isPresent()
and: "the details match"
with(ourMessage.get()) {
getMessage() == expectedText
getLevel() == Level.INFO
}
5. Checking for Formatted Messages
When we call getMessage on the LoggingEvent it returns the template rather than the formatted string. In the case of a simple message that’s all we need, but when we add parameters to our log message we must use getFormattedMessage to check the actual message logged.
So, let’s add a logInfoWithParameter method to our ClassWithLogger that logs a message using a template String and a parameter:
void logInfoWithParameter(String extraData) {
log.info("info message: {}", extraData);
}
Now, let’s write a test to invoke our new method:
def "when our subject logs an info message from a template then we validate the an info message was logged with a parameter"() {
when: "we invoke a method that logs info messages"
subject.logInfoWithParameter("parameter")
}
Finally, let’s check the first logged message to ensure the first item in LoggingEvent‘s getArgumentArray matches our parameter value and use getFormattedMessage to verify the complete message:
then: 'the details match for the first message in the list'
with (listAppender.list[0]) {
getMessage() == 'info message: {}'
getArgumentArray()[0] == 'parameter'
getFormattedMessage() == 'info message: parameter'
getLevel() == Level.INFO
}
Note that getMessage returns the template String whereas getFormattedMessage returns the actual message logged.
6. Summary
In this article, we learned how to use Logback’s ListAppender to collect our log messages and use the LoggingEvent to check for a simple message. We also learned how to verify messages logged using a templated string with a parameter, and the parameter value itself.
As usual, the source for this tutorial is over on GitHub.
The post How to Check For a Logged Message in a Spock Test first appeared on Baeldung.