测试akka actor在不保证顺序的情况下收到一组不同类型的消息

时间:2016-04-14 16:33:41

标签: scala akka scalatest akka-testkit

TL; DR:

我不知道如何测试Akka演员收到一组不同类型的消息而不保证消息的顺序。

具体细节:

我测试了一些域事件已发布到akka.event.EventStream。为此,我已为所有TestProbe子类订阅了DomainEvent

val eventBusTestSubscriber = TestProbe()(actorSystem)
actorSystem.eventStream.subscribe(eventBusTestSubscriber.ref, classOf[DomainEvent])

这样,我可以测试单个域事件到达EventStream 而不考虑其他可能的事件(避免脆弱的测试):

规格:

shouldPublishDomainEvent {
  event: WinterHasArrivedDomainEvent =>
    event.isReal shouldBe true
    event.year shouldBe expectedYear
}

助手特质:

def shouldPublishDomainEvent[EventType](eventAsserter: EventType => Unit)
  (implicit gotContext: GotContextTest, classTag: ClassTag[EventType]): Unit = {

  val receivedEvent = gotContext.eventBusTestSubscriber.receiveN(1).head

  receivedEvent match {
    case event: EventType =>
      eventAsserter(event)

    case _ =>
      shouldPublishDomainEvent(eventAsserter)
  }
}

对于我应该接收一组保证订单的同类型事件而不考虑其他可能的事件(避免脆弱的测试)的场景我也有一些测试:

规格:

val someoneDiedEventAsserter: SomeoneDiedDomainEvent => Unit = { event =>
  event.isReal shouldBe false
  event.episodeId shouldBe episodeId
}

val someoneDiedEventIdExtractor = (event: SomeoneDiedDomainEvent) => event.characterId

shouldPublishDomainEventsOfType(someoneDiedEventAsserter, someoneDiedEventIdExtractor)(characterIdsToDie)

助手特质:

def shouldPublishDomainEventsOfType[EventType, EventIdType](
  eventAsserter: EventType => Unit,
  eventIdExtractor: EventType => EventIdType
)(expectedEventIds: Set[EventIdType])
  (implicit gotContext: GotContextTest, classTag: ClassTag[EventType]): Unit = {

  if (expectedEventIds.nonEmpty) {
    val receivedEvent = gotContext.eventBusTestSubscriber.receiveN(1).head

    receivedEvent match {
      case event: EventType =>
        eventAsserter(event)
        val receivedEventId = eventIdExtractor(event)
        expectedEventIds should contain(receivedEventId)
        shouldPublishDomainEventsOfType(eventAsserter, eventIdExtractor)(expectedEventIds - receivedEventId)

      case _ =>
        shouldPublishDomainEventsOfType(eventAsserter, eventIdExtractor)(expectedEventIds)
    }
  }
}

现在的问题在于我必须测试我是否需要测试我发布一组具有不同类型但没有保证订单的事件

我不知道如何解决的问题是,在shouldPublishDomainEventsOfType的情况下,我有一个推断的EventType,它为我提供了类型,以便执行与之相关的特定断言这是eventAsserter: EventType => Unit中非常具体的类型。但由于我有不同的特定类型的事件,我不知道如何指定他们的类型等。

我尝试过一种基于包含断言功能的案例类的方法,但问题是一样的,我有点卡住了:

case class ExpectedDomainEvent[EventType <: DomainEvent](eventAsserter: EventType => Unit)

def shouldPublishDomainEvents[EventType](
  expectedDomainEvents: Set[ExpectedDomainEvent]
)(implicit chatContext: ChatContextTest): Unit = {

  if (expectedDomainEvents.nonEmpty) {
    val receivedEvent = chatContext.eventBusTestSubscriber.receiveN(1).head

    expectedDomainEvents.foreach { expectedDomainEvent =>

      val wasTheReceivedEventExpected = Try(expectedDomainEvent.eventAsserter(receivedEvent))

      if (wasTheReceivedEventExpected.isSuccess) {
        shouldPublishDomainEvents(expectedDomainEvents - receivedEvent)
      } else {
        shouldPublishDomainEvents(expectedDomainEvents)
      }
    }
  }
}

谢谢!

1 个答案:

答案 0 :(得分:0)

感谢Artur Soler:)

这里有解决方案以防万一:

case class ExpectedDomainEventsOfType[EventType <: DomainEvent, EventId](
  eventAsserter: EventType => Unit,
  eventIdExtractor: EventType => EventId,
  expectedEventIds: Set[EventId]
)(implicit expectedEventTypeClassTag: ClassTag[EventType]) {

  def isOfEventType(event: DomainEvent): Boolean = expectedEventTypeClassTag.runtimeClass.isInstance(event)

  def withReceivedEvent(event: DomainEvent): ExpectedDomainEventsOfType[EventType, EventId] = event match {
    case asExpectedEventType: EventType =>
      eventAsserter(asExpectedEventType)

      val eventId = eventIdExtractor(asExpectedEventType)
      expectedEventIds should contain(eventId)

      copy(expectedEventIds = expectedEventIds - eventId)
  }
}

def shouldPublishDomainEvents(
  expectedEvents: Set[ExpectedDomainEventsOfType[_, _]]
)(implicit gotContext: GotContextTest): Unit = {

  if (expectedEvents.nonEmpty) {
    val receivedEvent = gotContext.eventBusTestSubscriber.receiveN(1).head.asInstanceOf[DomainEvent]

    expectedEvents.find(expectedEventsOfType => expectedEventsOfType.isOfEventType(receivedEvent)) match {
      case Some(expectedEventsOfReceivedType) =>
        val expectedEventsWithoutTheReceived = expectedEventsOfReceivedType.withReceivedEvent(receivedEvent)

        if (expectedEventsWithoutTheReceived.expectedEventIds.isEmpty) {
          shouldPublishDomainEvents(expectedEvents - expectedEventsOfReceivedType)
        } else {
          shouldPublishDomainEvents(expectedEvents - expectedEventsOfReceivedType + expectedEventsWithoutTheReceived)
        }

      case None =>
        shouldPublishDomainEvents(expectedEvents)
    }
  }
}