I'm writing a UCI interpreter, as an Akka Finite State Machine. As per the specification, the interpreter must write its output to stdout
, and take its input from stdin
.
I have a test suit for the actor, and I can test some aspects (message related) of its behaviour, but I don't know how to capture the stdout
to make assertions, nor how to send it its input through stdin
. I've explored the scalatest API to the best of my abilities, but can't find how to achieve what I need.
This is the current test class:
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)
}
}
}
Of course, knowing that the interpreter can quit is useful... but not very useful. I need to test, for example, if sending the string isready
to the interpreter, it returns readyok
.
Is it possible that I am overcomplicating the test by using akka.testkit, instead of a simpler framework? I would like to keep using a single testing framework for simplicity, and I will need to test many other actor-related elements of the system, so if this could be solved without leaving the akka-testkit/scalatest domain, it would be fantastic.
Any help will be appreciated. Thanks in advance.
You need to change the design of your Actor.
The Actor should not read stdin
or write stdout
directly. Instead, give the actor objects in the Props
that provide input and accept output. stdin
could be something like () => String
that is called each time input is needed. stdout
could be String => Unit
that is called each time output is generated. Or you could use Streams
or similar constructs that are designed to be abstract sources and sinks of data.
In production code you pass objects that use stdin
and stdout
, but for test code you pass objects that read and write memory buffers. You can then check that the appropriate input is consumed by the Actor and that the appropriate output is generated by the Actor.