This time a short post about a simple Java Utility class to help out in Unit Testing the proper functioning of log statements in code under test.
Note: see also my notes section for an even simpler way of doing this! (when using spring-boot-test).
Most of the time when writing Unit Tests, you just tend to test for proper logic functioning of your code. Does it output what you expect using certain inputs or situations.
The log statements in your code are normally just for trouble shooting incidents, when you need to look at a trace of what happened, or what went wrong in what spot of the code.
But sometimes you actually want to check that in a certain case also a proper log message is logged. For example when you use the log (partially) as audit trail.
Most of the time we build services using spring-boot, and the Logback framework. So this example is for that code stack. You probably can use the same idea for other log frameworks also with a bit of tweaking.
See GitHub repo: https://github.com/atkaper/recording-log-appender-java-unit-test
Here you can find the helper class (in src/test/java):
An example (dummy) service (in src/main/java):
And an example Unit Test in (in src/test/java):
You can run the code (in the root folder of the project) using:
./mvnw clean install
See example output in the README.md on GitHub.
The “trick” of the MyRecordingLogAppender helper class is that it extends a normal ch.qos.logback.core.ConsoleAppender (line 28), and as soon as you create an instance of the helper, it will remove all existing console appenders (line 36), and put itself in there as the only appender (line 37).
Then in the doAppend method (line 45), the log events will arrive. And those are stored in a HashMap with a List of messages per log level.
The exposed methods to use in your tests are:
clearMessages – to wipe all stored log data.
containsMessage – to check if a certain message matching a substring exists.
findMessages – get collected log messages which match a given substring.
There are lots of comments in the code and examples. Should explain itself. And yes, I know, I could have used Lambda’s or other modern java features but that was not the point of this helper class. It functions fine as is 😉 Feel free to update this to your liking.
Thijs Kaper, 14th of May, 2020.