I want to create a simple unit test of expected logging (level and message) in a try/catch scenario.
One exception should log a info-message with a specified message and other exceptions should log warning messages.
I am using the following dependency: implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
My first attempt of this is done by logging with an object which I can mock in a unit test:
private val logger = KotlinLogging.logger {}
object MyLogger {
fun info(exception: Exception, msg: String) {
logger.info(exception) { msg }
}
fun warn(exception: Exception, msg: String) {
logger.warn(exception) { msg }
}
}
I do not like this solution as code is introduced for the purpose of testing only. Is there a way where I can test invocations of the logger directly?
I am using testImplementation("io.mockk:mockk-jvm:1.13.11") for mocking purposes
I was able to achieve this in the following way:
Create a class like this (in the same or a new .kt file):
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.AppenderBase
class TestLogAppender : AppenderBase<ILoggingEvent>() {
val logMessages = mutableListOf<ILoggingEvent>()
override fun append(eventObject: ILoggingEvent) {
logMessages.add(eventObject)
}
}
Update the test class to use the new TestLogAppender
private lateinit var testLogAppender: TestLogAppender
private val logger = LoggerFactory.getLogger("com.sap.pi.eureka.pandora.util") as Logger
@BeforeEach
fun setUp() {
testLogAppender = TestLogAppender()
testLogAppender.start()
logger.addAppender(testLogAppender)
}
@AfterEach
fun tearDown() {
logger.detachAppender(testLogAppender)
}
Use the testLogAppender in your @Test case. Example:
val warningLogs = testLogAppender.logMessages.filter { it.level == Level.WARN }
assertTrue(warningLogs.isNotEmpty())
(Optional) In case you want to assert on detailed log content including the stacktrace, I recommend introducing the following function
fun ILoggingEvent.toFormattedString(): String {
val s = "${this.level} - ${this.loggerName} - ${this.formattedMessage}"
if (this.throwableProxy != null) {
val throwable = (this.throwableProxy as ThrowableProxy).throwable
return s + "\n ${throwable.stackTraceToString()}"
}
return s
}
Then you can do assertions like the following
assertTrue(testLogAppender.logMessages.none { it.toFormattedString().contains("sensitiveValue") })
Hope that is useful to you!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With