17

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

我有一些 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 类中使用了 mixins,这似乎有点难以实现,因此任何做这些事情的想法都会非常有帮助。

更新: 我终于实施了一项测试,并决定与社区分享我的解决方案。我们不能直接模拟 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")
        }
    }
}

感谢埃里克的帮助。他的回答是解决问题的关键。

4

2 回答 2

11

一种可能性是使用 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 的限制之一)
于 2013-07-18T11:11:54.333 回答
3

好问题……好答案!我在使用 Mockito mixin 时遇到了一些麻烦。因此,我将 Eric 的方法与用于 Mockito 的 Java DSL 一起使用。如果有人对此变化感兴趣,这里是稍微修改的代码:

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")
  }
}
于 2015-12-19T23:13:40.010 回答