Как проверить поведение актора akka, если он читает из `stdin` и пишет в `stdout`?

Я пишу интерпретатор UCI в качестве конечного автомата Akka. В соответствии со спецификацией интерпретатор должен записывать свои выходные данные в stdout и получать входные данные из stdin.

У меня есть тестовый костюм для актера, и я могу проверить некоторые аспекты (связанные с сообщениями) его поведения, но я не знаю, как захватить stdout, чтобы сделать утверждения, и как отправить ему ввод через stdin. Я изучил API-интерфейс scalatest в меру своих возможностей, но не могу найти, как добиться того, что мне нужно.

Это текущий тестовый класс:

package org.chess

import akka.actor.ActorSystem
import akka.testkit.{TestKit, TestProbe}
import org.chess.Keyword.Quit
import org.scalatest.wordspec.AnyWordSpecLike
import org.scalatest.{BeforeAndAfterAll, Matchers}

import scala.concurrent.duration._
import scala.language.postfixOps

class UCIInterpreterSpec(_system: ActorSystem)
  extends TestKit(_system)
    with Matchers
    with AnyWordSpecLike
    with BeforeAndAfterAll {

  def this() = this(ActorSystem("UCIInterpreterSpec"))

  override def afterAll: Unit = {
    super.afterAll()
    shutdown(system)
  }

  "A UCI interpreter" should {

    "be able to quit" in {
      val testProbe = TestProbe()
      val interpreter = system.actorOf(UCIInterpreter.props)
      testProbe watch interpreter
      interpreter ! Command(Quit, Nil)
      testProbe.expectTerminated(interpreter, 3 seconds)
    }

  }

}

Конечно, знать, что интерпретатор может выйти, полезно... но не очень полезно. Мне нужно проверить, например, если отправить строку isready интерпретатору, он вернет readyok.

Возможно ли, что я слишком усложняю тест, используя akka.testkit вместо более простого фреймворка? Я хотел бы продолжать использовать единую среду тестирования для простоты, и мне нужно будет протестировать многие другие элементы системы, связанные с актерами, поэтому, если бы это можно было решить, не выходя из домена akka-testkit/scalest, это было бы фантастически.

Любая помощь будет оценена. Заранее спасибо.


person Bruno Unna    schedule 02.07.2019    source источник
comment
Удачи с UCI, откройте этот материал, если он работает;)   -  person yǝsʞǝla    schedule 03.07.2019
comment
Спасибо! Он доступен по адресу github.com/bruno-unna/chess (в настоящее время работает над веткой uci-implementation) . Я еще не определил лицензию, но, конечно, она будет с открытым исходным кодом.   -  person Bruno Unna    schedule 03.07.2019


Ответы (1)


Вам нужно изменить дизайн вашего Актера.

Актер не должен читать stdin или писать stdout напрямую. Вместо этого дайте актеру объекты в Props, которые обеспечивают ввод и принимают вывод. stdin может быть чем-то вроде () => String, который вызывается каждый раз, когда требуется ввод. stdout может быть String => Unit, который вызывается каждый раз, когда генерируется вывод. Или вы можете использовать Streams или подобные конструкции, предназначенные для использования в качестве абстрактных источников и приемников данных.

В производственном коде вы передаете объекты, которые используют stdin и stdout, но для тестового кода вы передаете объекты, которые считывают и записывают буферы памяти. Затем вы можете проверить, что актор потребляет соответствующий ввод и что актор генерирует соответствующий вывод.

person Tim    schedule 02.07.2019
comment
Полностью согласен с этим ответом в отношении использования инверсии управления для достижения этого. т.е. передать что-то абстрактное, что можно прочитать, вместо жесткой зависимости stdin. В некоторых случаях, когда у вас нет этого элемента управления, например, если вы тестируете библиотеку, вы можете захотеть переназначить stdin/stdout с помощью таких методов, как: docs.oracle.com/javase/7/docs/api/java/lang/ System.setIn(...InputStream...). - person yǝsʞǝla; 03.07.2019
comment
Возможно, более надежным подходом к переназначению будет: stackoverflow.com/a/56523167/1972909. - person yǝsʞǝla; 03.07.2019