Unit test logger messages using specs2 + scalalogging

logging, scala, unit-testing

Solution

One possibility is to use Mockito to check method calls:

class Testable extends Logging {
  def method() = {
    // some method calls
    logger.info("Message1")
  }
}

class TestableSpec extends Specification with ShouldMatchers with Mockito {
   "Testable instance" should {
     "print proper log message during method call" in {
       val mockLogger = mock[Logger]
       val testable = new Testable {
         // switch the logger with a mock instance
         override val logger = mockLogger
       }
       testable.method()
       there was one(mockLogger).info("Message1")
     }
  }
}

This is the main idea but you might have to adapt it depending on your exact traits and logging framework:

- logger must be overridable

- the info method must not be final (one of Mockito's limitations)

Problem

I have a little problem here and really have no idea how to implement unit testing for logger messages. Of course, it sounds a little weird, but for me it's really interesting topic. But let me be more specific. I have some scala class and test specification: ``` class Testable extends Logging { def method() = { // some method calls logger.info("Message1") } } class TestableSpec extends Specification with ShouldMatchers with Mockito { "Testable instance" should { // some important tests "print proper log message during method call" in { // And how to test that logger really prints proper message ("Message1")? } } } ``` My first thought was to intercept underlying logger engine messages but it seems a little hard thing to implement due to usage of mixins in Testable class, therefore any ideas to do such things would be very helpful. UPDATE: I finally implemented a test and decided to share my solution with community. We cannot mock scalalogging.Logger class directly because it's final but we still can mock underlying slf4j Logger. To clarify an idea: ``` class Testable extends Logging { def foo() = { // ... logger.info("Foo has been called") } } // Another imports are omitted. import com.typesafe.scalalogging.slf4j.Logger import org.slf4j.{Logger => Underlying} class TestableSpec extends Specification with Mockito with ShouldMatchers { def initTestable(mocked: Underlying): Testable = { new Testable() { override lazy val logger = Logger(mocked) } } "Testable instance" should { "invoke logger with a proper message" in { val mocked = mock[Underlying] mocked.isInfoEnabled returns true // Should be set to true for test initTestable(mocked).foo() there was one(mocked).info("Foo has been called") } } } ``` Thanks Eric for his help. His answer was a key to the solution.

Original source