Почему `TestFSMRef.receive должен бросать [Exception]` периодически сбой

Привет коллеги-кодеры и уважаемые гуру,

У меня есть действующее лицо, реализующее FSM, которое требуется для создания исключения IOException для определенных сообщений в определенном состоянии (Busy), которое должно быть перезапущено его Supervisor.

отрывок:

case class ExceptionResonse(errorCode: Int)


when(Busy) {
    case ExceptionResponse(errorCode) =>
      throw new IOException(s"Request failed with error code $errorCode")
}

Я пытаюсь проверить это поведение, используя TestActorRef и вызывая receive непосредственно для этого, ожидая, что получение вызовет IOException.

case class WhenInStateBusy() extends TestKit(ActorSystem()) with After {
  val myTestFSMRef = TestFSMRef(MyFSM.props)

  ...

  def prepare: Result = {
    // prepares tested actor by going through an initialization sequence
    // including 'expectMsgPfs' for several messages sent from the tested FSM
    // most of my test cases depend on the correctness of that initialization sequence

    // finishing with state busy
    myTestFSMRef.setState(Busy)

    awaitCond(
      myTestFSMRef.stateName == Busy, 
      maxDelay, 
      interval, 
      s"Actor must be in State 'Busy' to proceed, but is ${myTestFSMRef.stateName}"
    )
    success
  }

  def testCase = this {
    prepare and {
      myTestFSMRef.receive(ExceptionResponse(testedCode)) must throwAn[IOException]
    }
  }
}

Примечание. Последовательность инициализации гарантирует, что тестируемый FSM полностью инициализирован и настроил свое внутреннее изменяемое состояние. Состояние Busy можно оставить только тогда, когда актер получает сообщение определенного типа, которое в моей тестовой настройке должно быть предоставлено тестовым набором, поэтому я почти уверен, что FSM находится в правильном состоянии.

Теперь на моем сервере Jenkins (Ubuntu 14.10) этот тестовый пример терпит неудачу примерно в 1 из 20 попыток (-> исключение не выдается). Однако на моей машине разработки (Mac Os X 10.10.4) я не могу воспроизвести ошибку. Так что отладчик мне не помогает.

Тесты запускаются последовательно, и после каждого примера тестовая система выключается.

  • Java версии 1.7.0_71
  • Скала версии 2.11.4
  • Акка версии 2.3.6
  • Спец2 версия 2.3.13

Кто-нибудь может объяснить, почему иногда вызов myTestActorRef.receive(ExceptionResponse(testedCode)) не приводит к Exception?


person Sascha Kolberg    schedule 13.08.2015    source источник
comment
Вы не показываете, где создается myTestActorRef. Это опечатка myTestFSMRef или это два разных объекта?   -  person mattinbits    schedule 13.08.2015
comment
@mattinbits да, это была опечатка, исправил вопрос   -  person Sascha Kolberg    schedule 13.08.2015
comment
Вы были правы в отношении awaitCond, я смотрел на Java API. Тем не менее, AssertException провалит ваш тест, верно?   -  person mattinbits    schedule 13.08.2015
comment
да, AssertionException приводит к сбою теста.   -  person Sascha Kolberg    schedule 13.08.2015
comment
Я нашел обходной путь для своей проблемы в варианте ответа на этот вопрос. Тем не менее, я до сих пор не понимаю, почему это не работает.   -  person Sascha Kolberg    schedule 13.08.2015


Ответы (1)


Это действительно сложный вопрос: мой главный подозреваемый заключается в том, что Актер еще не инициализирован. Почему это? При реализации system.actorOf (который используется TestFSMRef.apply()) стало ясно, что может быть только один объект, который отвечает за фактический запуск Актера, и это его родитель. Я пробовал много разных вещей, и все они были в чем-то несовершенны.

Но почему этот тест проваливается?

Основной ответ заключается в том, что с кодом, который вы показываете, не гарантируется, что во время выполнения setState FSM уже был инициализирован. Особенно на (маломощных) ящиках Дженкинса может случиться так, что актер-хранитель не запланирован для работы в течение измеримого периода времени. Если это так, то оператор startWith в вашем FSM переопределит оператор setState, потому что он запускается позже.

Решением этой проблемы может быть отправка еще одного сообщения в FSM и ожидание правильного ответа перед вызовом setState.

person Roland Kuhn    schedule 15.08.2015
comment
Спасибо, это звучит вполне правдоподобно :) Мне нужно будет проверить в офисе, делает ли это фактический тестовый пример или нет. - person Sascha Kolberg; 15.08.2015
comment
Боюсь, это не то. Тестовый пример ожидает несколько других сообщений от тестируемого актора без взаимодействия с конечным автоматом перед запуском фактического теста. Я обновил свой вопрос, чтобы максимально приблизиться к фактическому коду, поскольку, к сожалению, я не могу вставить сам фактический код. - person Sascha Kolberg; 17.08.2015
comment
Тогда единственное другое объяснение состоит в том, что FSM делает что-то в фоновом режиме, что переопределяет состояние. Как правило, не рекомендуется смешивать синхронные и асинхронные взаимодействия, и наличие TestFSMRef не гарантирует синхронную обработку всех сообщений, заметными исключениями являются действия супервизора и внешние взаимодействия. - person Roland Kuhn; 17.08.2015