2015-09-23 6 views
2

В качестве учебного упражнения для Akka FSM я смоделировал упрощенный процесс обработки заказов в кафе. Прилагается диаграмма перехода состояния. Тем не менее, один из тестовых случаев, которые я написал, и я не понимаю, почему.Почему мое мероприятие Akka FSM тайм-аут?

ФСМ (классы случае не показаны для краткости):

class OrderSystem extends Actor with ActorLogging with LoggingFSM[State, Data] { 
    startWith(OrderPending, Data(OrderPending, PaymentPending)) 

    when(OrderPending) { 
    case Event(BaristaIsBusy, _) => stay 
    case Event(BaristaIsAvailable(_, PaymentPending), _) => goto(OrderPlaced) using Data(stateName, PaymentPending) 
    case Event(b: BaristaIsAvailable, _) => goto(OrderReady) 
    } 

    val waiting = Data(OrderPlaced, PaymentAccepted) 

    when(OrderPlaced) { 
    case Event(b: BaristaIsAvailable, `waiting`) => println("1"); goto(OrderReady) 
    case Event(b: BaristaIsBusy, `waiting`) => println("2"); goto(OrderPending) using `waiting` 
    case Event(_, Data(_, PaymentDeclined)) => println("3"); goto(OrderClosed) 
    case Event(_, Data(_, PaymentPending)) => println("4"); stay 
    } 

    when(OrderReady) { 
    case Event(HappyWithOrder, _) => goto(OrderClosed) 
    case Event(NotHappyWithOrder, _) => goto(OrderPending) using Data(stateName, PaymentAccepted) 
    } 

    when(OrderClosed) { 
    case _ => stay 
    } 

    whenUnhandled { 
    case Event(e, s) => { 
     // state name is available as 'stateName' 
     log.warning("Received unhandled request {} in state {}/{}", e, stateName, s) 
     stay 
    } 
    } 

    // previous state data is available as 'stateData' and next state data as 'nextStateData' 
    // not necessary as LoggingFSM (if configured) will take care of logging 
    onTransition { 
    case _ -> nextState => log.info("Entering state: {} with payment activity: {} from state: {} with payment activity: {}.", 
     nextState, stateData.paymentActivity, nextStateData.fromState, nextStateData.paymentActivity) 
    } 

    initialize() 
} 

В противном случае тест:

it should "stay in OrderPlaced state as long as customer has not paid" in { 
    val orderSystem = system.actorOf(Props[OrderSystem]) 
    orderSystem ! BaristaIsAvailable(OrderPending, PaymentPending) 

    orderSystem ! SubscribeTransitionCallBack(testActor) 

    expectMsg(CurrentState(orderSystem, OrderPlaced)) 

    orderSystem ! BaristaIsAvailable(OrderPlaced, PaymentPending) 

    expectMsg(CurrentState(orderSystem, OrderPlaced)) 
} 

Журналы:

2015-09-22 23:29:15.236 [order-system-akka.actor.default-dispatcher-2] [DEBUG] n.a.s.o.OrderSystem - processing Event(BaristaIsAvailable(OrderPending,PaymentPending),Data(OrderPending,PaymentPending)) from Actor[akka://order-system/system/testActor1#-2143558060] 
2015-09-22 23:29:15.238 [order-system-akka.actor.default-dispatcher-2] [INFO ] n.a.s.o.OrderSystem - Entering state: OrderPlaced with payment activity: PaymentPending from state: OrderPending with payment activity: PaymentPending. 
2015-09-22 23:29:15.239 [order-system-akka.actor.default-dispatcher-2] [DEBUG] n.a.s.o.OrderSystem - transition OrderPending -> OrderPlaced 
4 
2015-09-22 23:29:15.242 [order-system-akka.actor.default-dispatcher-2] [DEBUG] n.a.s.o.OrderSystem - processing Event(BaristaIsAvailable(OrderPlaced,PaymentPending),Data(OrderPending,PaymentPending)) from Actor[akka://order-system/system/testActor1#-2143558060] 
[31m- should stay in OrderPlaced state as long as customer has not paid *** FAILED ***[0m 
[31m java.lang.AssertionError: assertion failed: timeout (3 seconds) 

enter image description here

+1

вы не посылать назад любые отвечено назад от вашего ФШМ, это нормально, что ваш expectMsg терпит неудачу – kosii

ответ

2

будет SubscribeTransitionCallBack только доставьте CurrentState один раз, а затем только Transition обратные вызовы.

Вы могли бы попытаться сделать это:

it should "stay in OrderPlaced state as long as customer has not paid" in { 

    val orderSystem = TestFSMRef(new OrderSystem) 

    orderSystem ! SubscribeTransitionCallBack(testActor) 
    // fsm first answers with current state 
    expectMsgPF(1.second. s"OrderPending as current state for $orderSystem") { 
    case CurrentState('orderSystem', OrderPending) => ok 
    } 

    // from now on the subscription will yield 'Transition' messages 
    orderSystem ! BaristaIsAvailable(OrderPending, PaymentPending) 
    expectMsgPF(1.second, s"Transition from OrderPending to OrderPlaced for $orderSystem") { 
    case Transition(`orderSystem`, OrderPending, OrderPlaced) => ok 
    } 

    orderSystem ! BaristaIsAvailable(OrderPlaced, PaymentPending) 
    // there is no transition, so there should not be a callback. 
    expectNoMsg(1.second) 

/* 
    // alternatively, if your state data changes, using TestFSMRef, you could check state data blocking for some time 
    awaitCond(
    p = orderSystem.stateData == ???, 
    max = 2.seconds, 
    interval = 200.millis, 
    message = "waiting for expected state data..." 
) 
    // awaitCond will throw an exception if the condition is not met within max timeout 
*/ 
    success 
} 
+0

я вижу зонд, используемый в [этой] (https: // github.com/tombray/akka-fsm-examples/blob/master/src/test/scala/com/tombray/examples/akka/ElevatorActorSpec.scala). Будет ли это работать на мое дело? 'val probe = TestProbe(); probe.expectMsg' –

+0

@AbhijitSarkar. Конечно, есть способ использовать «TestProbe» в вашем тестовом примере, который будет работать. Но это делает вещи более сложными, чем что-то улучшать. –

+0

Hm. И последний вопрос. В вышеприведенных журналах я вижу, что когда я отправил 2-е событие, он не только изменил состояние, но также совпадал с футляром 4 (напечатан как 4 в журналах). Является ли ожидаемое поведение тем, что если я использую 'goto' с событием, для которого нет совпадения, оно входит в это состояние, но не соответствует операторам' case'. Если 'goto' используется с событием, которое имеет соответствие, оно переходит в новое состояние, а также соответствует' case'? –

 Смежные вопросы

  • Нет связанных вопросов^_^