Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a simple unit test of kotlin-loggin

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

like image 607
jactor-rises Avatar asked Oct 20 '25 11:10

jactor-rises


1 Answers

I was able to achieve this in the following way:

  1. 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)
        }
    }
    
  2. 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)
        }
    
  3. Use the testLogAppender in your @Test case. Example:

    val warningLogs = testLogAppender.logMessages.filter { it.level == Level.WARN }
    assertTrue(warningLogs.isNotEmpty())
    
  4. (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!

like image 171
Domi W Avatar answered Oct 22 '25 07:10

Domi W



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!