【问题标题】:Testing event submission into a service测试事件提交到服务
【发布时间】:2018-04-18 10:55:46
【问题描述】:

我正在测试一个执行一些异步计算的服务实现。这是特点

trait Event
trait Service{
    def processEvents(ex: Seq[Event])
}

我有以下实现:

//Business logic
trait EventHandler{
     def handleEvent(e: Event)
}

//Threading
class AsynchronousService(private final val handler: EventHandler)
     extends Service {
     def processEvents(ex: Seq[Event]) = {
        //execute handler.handleEvent(e) in some 
        //thread pool or so...
        //should not do any processing in case of ex.isEmpty
     }
}

我遇到的问题是在测试AsynchronousService 是否为空Seq[Event] 时。这是我在 scalatest 中的测试:

it("should verify no processing in case of empty event seq"){
    val mockedHandler = //mock with Mockito
    val service = new AsynchronousService(mockedHandler)
    service.processEvents(Seq())
    Thread.sleep(1000) //<--- waiting some time. looks ugly
    verifyZeroInteraction(mockedHandler)
}

问题在于Thread.sleep(1000)。只要AsynchronousService 对另一个线程执行调度/提交/或其他操作,不等待一段时间就得出结论然后执行verifyZeroInteraction(mockedHandler) 是不正确的。但是Thread.sleep(1000) 看起来很奇怪。

问题:可能有Scalatest 设施吗?或者如何正确编写这样的测试?

【问题讨论】:

    标签: scala testing mockito scalatest


    【解决方案1】:

    你可以使用ScalaFutures

    import org.scalatest.concurrent.ScalaFutures

    例如:

     val futEvents: Future[???] = service.processEvents(Seq())
    
     whenReady(futEvents, testTimeout) { events =>
        // do your testing
        verifyZeroInteraction(mockedHandler)
      }
    

    这当然期望service.processEvents(Seq()) 返回一个Future!

    这是一个很好的博客,描述了这种可能性和其他可能性:testing-future-objects-scalatest

    【讨论】:

    • 谢谢。但是我们可以做一些比Thread.sleep 更漂亮的事情,以防万一“一劳永逸”而不返回未来?
    • 你根本不需要Thread.sleep!只要您留在 whenReady 子句中。我在答案中添加了您的测试
    【解决方案2】:

    我要做的是将 ThreadPool/Executor/Whatever 作为依赖项传递,然后您可以对它进行存根/模拟,因此它会同步执行,因此您的测试会更好

    //Business logic
    trait EventHandler{
      def handleEvent(e: Event)
    }
    
    //Threading
    class AsynchronousService(handler: EventHandler, executor: Executor)
      extends Service {
      def processEvents(ex: Seq[Event]) = {
        executor.execute(handler.handleEvent(<blah>))
      }
    }
    

    然后是测试

    it("should verify no processing in case of empty event seq"){
      val mockedHandler = //mock with Mockito
      val mockExecutor = //mock with Mockito
      when(mockExecutor.execute(any)) thenAnswer { invocation =>
        val codeBlock: () => Unit = invocation.getArgument(0)
        codeBlock()
      }
      val service = new AsynchronousService(mockedHandler, mockExecutor)
      service.processEvents(Seq())
      verifyZeroInteraction(mockedHandler)
    }
    

    当然,我正在编造 Executor 类,你应该调整你的代码以使用你正在使用的任何东西,但这就是想法。 您也可以为 Executor 使用存根而不是模拟,但根据您使用的模拟可能更容易,该代码假定 Scala 2.12 BTW,否则 thenAnswer 的语法不同

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 2014-11-14
      • 2020-04-21
      • 1970-01-01
      • 2018-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-09
      相关资源
      最近更新 更多