I'd like to unit test an actor that binds its self to local port using the Tcp IO manager e.g
IO(Tcp) ! Bind(self, new InetSocketAddress("localhost", port))
What I'd ideally like to do is get a TestProbe in place of the IO manager. This will have the added benefit of a unit test not opening a TCP connection.
Is this possible, and if so, could someone please provide an example of how to do it?
There's probably a few ways to achieve this. I'll pass along a couple in hopes that one works for you. The thing to keep in mind that calling IO(Tcp)
just produces an ActorRef
. So all you need to do is find a way to mock/step in front of that call and then work with that actor ref. Here are a couple of code samples showing a couple of solutions (sort of DI like):
This first one sets up a method to return the tcp manager and then overrides it in the unit test.
class Actor1 extends Actor{
import context._
import Tcp._
def receive = {
case "connect" =>
tcp ! Bind(self, new InetSocketAddress("localhost", 8080))
}
def tcp:ActorRef = IO(Tcp)
}
class Actor1Spec(_system:ActorSystem) extends TestKit(_system) with Specification{
import Tcp._
def this() = this(ActorSystem("test"))
trait scoping extends Scope{
val tcpProbe = TestProbe()
val testRef = TestActorRef(new Actor1{
override def tcp = tcpProbe.ref
})
}
"A request to connect" should{
"send a message to connect to the Tcp Manager" in new scoping{
testRef ! "connect"
tcpProbe.expectMsg(Bind(testRef, new InetSocketAddress("localhost", 8080)))
}
}
}
This second one is very similar but instead passes in the tcp manager into the constructor.
class Actor2(tcpManager:ActorRef) extends Actor{
import context._
import Tcp._
def receive = {
case "connect" =>
tcpManager ! Bind(self, new InetSocketAddress("localhost", 8080))
}
}
class Actor2Spec(_system:ActorSystem) extends TestKit(_system) with Specification{
import Tcp._
def this() = this(ActorSystem("test"))
trait scoping extends Scope{
val tcpProbe = TestProbe()
val testRef = TestActorRef(new Actor2(tcpProbe.ref))
}
"A request to connect" should{
"send a message to connect to the Tcp Manager" in new scoping{
testRef ! "connect"
tcpProbe.expectMsg(Bind(testRef, new InetSocketAddress("localhost", 8080)))
}
}
}
Either of these could work for you, but again, there is no real magic here as long as you understand that the call to IO(Tcp)
is just returning an ActorRef
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With