1

TL;博士:

我不知道如何在不保证消息顺序的情况下测试 Akka actor 是否接收到一组不同类型的消息。

规格:

我正在测试一些域事件是否发布到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)
      }
    }
  }
}

谢谢!

4

1 回答 1

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)
    }
  }
}
于 2016-04-19T09:54:34.290 回答