使用specs2 + scalalogging对单元测试记录器消息进行测试

时间:2013-07-18 09:26:24

标签: unit-testing scala logging

我这里有一点问题,实在不知道如何为记录器消息实现单元测试。当然,这听起来有点奇怪,但对我来说这是一个非常有趣的话题。但让我更具体一点。

我有一些scala类和测试规范:

 
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")? 
    }
  }
}

我的第一个想法是拦截底层记录器引擎消息,但由于在Testable类中使用了mixin,因此实现起来似乎有点困难,因此任何做这些事情的想法都会非常有用。

更新 我终于实施了一项测试,并决定与社区分享我的解决方案。我们不能直接模拟scalalogging.Logger类,因为它是最终的,但我们仍然可以模拟底层的slf4j Logger。澄清一个想法:

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")
        }
    }
}

感谢Eric的帮助。他的回答是解决方案的关键。

2 个答案:

答案 0 :(得分:8)

一种可能性是使用Mockito检查方法调用:

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")
     }
  }
}

这是主要想法,但您可能需要根据您的确切特征和日志框架进行调整:

  • 记录器必须可以覆盖
  • 信息方法不能是最终的(Mockito的限制之一)

答案 1 :(得分:0)

好问题......好回答!我在Mockito mixin上遇到了一些麻烦。所以我使用Eric的方法使用Java DSL for Mockito。如果有人对此变体感兴趣,这里是稍微修改过的代码:

import com.typesafe.scalalogging.{LazyLogging, Logger, StrictLogging}
import org.mockito.Mockito
import org.mockito.Mockito._
import org.slf4j.{Logger => Underlying}

class Testable extends LazyLogging {
    def foo() = {
    logger.info("Foo has been called")
    }
}

import org.junit.runner.RunWith
import org.scalatest.{BeforeAndAfterEach, FunSuite}
import org.scalatest.junit.JUnitRunner
import org.scalatest.matchers.ShouldMatchers


@RunWith(classOf[JUnitRunner])
class  LoggerTest
  extends FunSuite with ShouldMatchers with BeforeAndAfterEach {


    def initTestable(mocked: Underlying): Testable = {
    new Testable() {
        override lazy val logger = Logger(mocked)
    }
    }

  test("the mockito stuff") {
    val mocked = Mockito.mock(classOf[Underlying])
    when(mocked.isInfoEnabled()).thenReturn(true)
    initTestable(mocked).foo()
    verify(mocked).info("Foo has been called")
  }
}