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