Unit testing 使用specs2和x2B的单元测试记录器消息;鳞片法

Unit testing 使用specs2和x2B的单元测试记录器消息;鳞片法,unit-testing,scala,logging,Unit Testing,Scala,Logging,我在这里有一个小问题,真的不知道如何实现日志消息的单元测试。当然,这听起来有点奇怪,但对我来说,这是一个非常有趣的话题。但让我更具体一点 我有一些scala类和测试规范: 类可测试扩展日志记录{ def方法()={ //一些方法调用 logger.info(“Message1”) } } 类TestableSpec使用ShouldMatchers和Mockito扩展了规范{ “可测试实例”应{ //一些重要的测试 中的“在方法调用期间打印正确的日志消息”{ //如何测试记录器是否真正打印出正确的

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

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

类可测试扩展日志记录{
def方法()={
//一些方法调用
logger.info(“Message1”)
}
}
类TestableSpec使用ShouldMatchers和Mockito扩展了规范{
“可测试实例”应{
//一些重要的测试
中的“在方法调用期间打印正确的日志消息”{
//如何测试记录器是否真正打印出正确的消息(“Message1”)?
}
}
}
我的第一个想法是截获底层日志引擎消息,但由于在可测试类中使用了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")
        }
    }
}

谢谢埃里克的帮助。他的答案是解决方案的关键。

一种可能性是使用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")
     }
  }
}
这是主要的想法,但您可能必须根据您的具体特征和日志框架进行调整:

  • 记录器必须是可重写的
  • info方法不能是最终的(Mockito的限制之一)

    • 好问题。。。回答得好!我在Mockito mixin上遇到了一些麻烦。因此,我将Eric的方法用于Mockito的JavaDSL。如果有人对这种变化感兴趣,下面是稍加修改的代码:

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

      埃里克,看来该走了,谢谢!至少它不需要对可测试类进行任何更改,我不知道我可以用新的类{}语法重写内部类的值。谢谢到目前为止,scalalogging
      Logger
      类是最终类,模拟它不起作用