I have an actor of the form:
class TestActor(repo: Repo) extends Actor {
implicit val executionContext: ExecutionContext = context.dispatcher
def receive: Receive = {
...
}
def testMethod(stringSeq: Seq[String]): String =
...
}
}
I want to test the method testMethod only.
I am trying to write the test case as:
class TestActorSpec
extends TestKit(ActorSystem("TestActorSpec"))
with WordSpecLike
with Matchers
with JsonSupport
with MockitoSugar
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll: Unit = TestKit.shutdownActorSystem(system)
implicit val futureDuration: FiniteDuration = 60.seconds
implicit val timeout: Timeout = 10.seconds
val mockedRepo = mock[Repo]
val testActorRef: ActorRef = system.actorOf(
TestActor.props(mockedRepo)
)
"TestActorSpec" should {
"be able to get data " in {
}
}
}
How can i access the method testMethod from testActorRef ?
I also tried:
TestActorRef[TestActor].underlyingActor.testMethod(Seq("data"))
It does not work for me.
Actors should be tested via messages you send to them. But you still can test methods in isolation if the method is not accessing any actor specific values, like context.
You can move testMethod into companion object and test it from there.
class TestActor(repo: Repo) extends Actor {
implicit val executionContext: ExecutionContext = context.dispatcher
def receive: Receive = {
// call TestActor.testMethod
}
}
object TestActor {
def testMethod(stringSeq: Seq[String]): String = ???
}
Related
I have someTrait that extends Actor ( becuase it using some actor ref inside)
trait DemoService extends Actor {
def actorRefFactory = context
implicit def executionContext = actorRefFactory.dispatcher
val demoRoute = {
get {
pathSingleSlash {
complete(index)
} ~
path("ping") {
complete("PONG!")
}
Now I am Writing Spec file for same.
TestServiceSpec extends FlatSpec with Spec2RouteTest with DemoService
gettting error
could not find implicit value for parameter ta: TestServiceSpec
.this.TildeArrow[spray.routing.RequestContext,Unit]"
If I use ScalaTestRouteTest also I get this error.
I've designed an actor that should send its' actorRef to another actor on prestart:
class MyActor(notifier: ActorRef) extends Actor {
override def preStart(): Unit = {
notifier ! Register(self)
}
...
}
case class Register(actor: ActorRef)
Then I've written a specification for this Actor:
class MyActorSpec extends TestKit(ActorSystem("MyActorSpec"))
with ImplicitSender
with WordSpecLike
with Matchers
with BeforeAndAfterAll {
"MyActor" should {
val notifier = TestProbe()
"register itself in notifier" in {
val myActor = system.actorOf(Props(classOf[MyActor], notifier.ref))
notifier.expectMsg(Register(myActor))
}
}
}
When I run my test, it fails with the following message: assertion failed: expected Register(Actor[akka://MyActorSpec/user/$b#1849780829]), found Register(Actor[akka://MyActorSpec/user/$a#1143150267])
So, it seems that ActorRef obtained via self inside MyActor is not equal to ActorRef obtained via system.actorOf in my test. Any suggestions?
The following code is working just fine for me (the test passes):
class MyActor(notifier: ActorRef) extends Actor {
override def preStart(): Unit = {
notifier ! Register(self)
}
override def receive: Receive = {
case _ =>
}
}
case class Register(actor: ActorRef)
class MyActorSpec extends TestKit(ActorSystem("MyActorSpec")) with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
"MyActor" should {
val notifier = TestProbe()
"register itself in notifier" in {
val myActor = system.actorOf(Props(new MyActor(notifier.ref)))
notifier.expectMsg(Register(myActor))
}
}
}
The code you posted ideally should not even compile at:
val myActor = system.actorOf(Props(classOf[MyActor], notifier))
Because the constructor expects an ActorRef which we are not passing. But correcting it, it works:
class MyActorSpec extends TestKit(ActorSystem("MyActorSpec"))
with ImplicitSender
with WordSpecLike
with Matchers
with BeforeAndAfterAll {
"MyActor" should {
val notifier = TestProbe()
"register itself in notifier" in {
val myActor = system.actorOf(Props(classOf[MyActor], notifier.ref))
notifier.expectMsg(Register(myActor))
}
}
}
For a second, just to make sure that there is no special magic happening because of ActorProbe, I wrote plain old actor class below.
object Prac {
def main(args: Array[String]) {
val system = ActorSystem("HelloSystem")
val myActor = system.actorOf(Props(classOf[MainActor]))
}
}
class MyActor(notifier: ActorRef) extends Actor {
override def preStart(): Unit = {
notifier ! Register(self)
}
override def receive: Receive = {
case x => println("My Actor ->"+x)
}
}
case class Register(actor: ActorRef)
class MainActor extends Actor{
val actor = context.actorOf(Props(classOf[MyActor], self))
override def receive = {
case Register(x) =>
println(actor == x)
context.system.shutdown()
}
}
And it prints true. So there is nothing wrong with your programme.
I've figured it out. It was because I've used shared TestProbe in several test cases, in which I create different instances of MyActor.
class MyActorSpec extends TestKit(ActorSystem("MyActorSpec"))
with ImplicitSender
with WordSpecLike
with Matchers
with BeforeAndAfterAll {
"MyActor" should {
val notifier = TestProbe()
"register itself in notifier" in {
val myActor = system.actorOf(Props(classOf[MyActor], notifier.ref))
notifier.expectMsg(Register(myActor))
}
"do some useful work" in {
val myActor = system.actorOf(Props(classOf[MyActor], notifier.ref))
....
}
}
}
Instead, using a fresh instance of TestProbe for each test case helped.
class MyActorSpec extends TestKit(ActorSystem("MyActorSpec"))
with ImplicitSender
with WordSpecLike
with Matchers
with BeforeAndAfterAll {
"MyActor" should {
"register itself in notifier" in {
val notifier = TestProbe()
val myActor = system.actorOf(Props(classOf[MyActor], notifier.ref))
notifier.expectMsg(Register(myActor))
}
"do some useful work" in {
val notifier = TestProbe()
val myActor = system.actorOf(Props(classOf[MyActor], notifier.ref))
....
}
}
}
Anyway, thanks to all for proving that for single test case it works well.
I have a Akka actor as follows; it receives a message and returns a HTTP response.
I am having trouble testing the interaction with Dispatch Http, it is a nice library but seems difficult to test.
class Service(serviceUrl:String) extends Actor with ActorLogging {
implicit val ec = context .dispatcher
override def receive: Receive = {
case Get(ids) => request(ids)
}
private def request(ids:Seq[Int]):Unit = {
val requestUrl = buildRequestUrl(ids)
val request = url(requestUrl).GET
Http(request) pipeTo sender()
}
}
One way would be to do something like this with your actor:
case class Get(ids:Seq[Int])
class Service(serviceUrl:String) extends Actor with ActorLogging {
implicit val ec = context .dispatcher
def receive: Receive = {
case Get(ids) => request(ids)
}
def request(ids:Seq[Int]):Unit = {
val requestUrl = buildRequestUrl(ids)
val request = url(requestUrl).GET
executeRequest(request) pipeTo sender()
}
def executeRequest(req:Req) = Http(req)
def buildRequestUrl(ids:Seq[Int]):String = s"http://someurl.com/?ids=${ids.mkString(",")}"
}
Here, I'm providing a method, executeRequest that all it does it execute the http request and return the result. This method will be overridden in my test like so:
class ServiceTest extends TestKit(ActorSystem("test")) with SpecificationLike with Mockito with ImplicitSender{
trait scoping extends Scope{
def mockResult:Response
var url:String = ""
val testRef = TestActorRef(new Service(""){
override def executeRequest(req:Req) = {
url = req.toRequest.getUrl()
Future.successful(mockResult)
}
})
}
"A request to service " should{
"execute the request and return the response" in new scoping{
val mockedResp = mock[Response]
def mockResult = mockedResp
testRef ! Get(Seq(1,2,3))
expectMsg(mockedResp)
url ==== s"http://someurl.com/?ids=${URLEncoder.encode("1,2,3")}"
}
}
It's a bit crude but can be effective.
Below class is causing an error at line new HelloWorld :
Exception in thread "main" akka.actor.ActorInitializationException: You cannot create an instance of [HelloWorld] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
at akka.actor.ActorInitializationException$.apply(Actor.scala:219)
at akka.actor.Actor$class.$init$(Actor.scala:436)
at HelloWorld.<init>(HelloWorld.scala:4)
at Driver$.main(HelloWorld.scala:38)
at Driver.main(HelloWorld.scala)
So I try : val hw = actorOf(new HelloWorld)
But this causes a compiler error :
not found: value actorOf
How should HelloWorld below be implemented ?
Reading other Scala docs an act method is requried to be defined within the class that extends Actor and then invoke the start method on this class, is there a reason for using actorOf instead of defining an act method ?
Below class is taken from Scala akka docs http://doc.akka.io/docs/akka/2.2.0/scala.html :
import akka.actor.Actor
import akka.actor.Actor._
import akka.actor.Props
class HelloWorld extends Actor {
override def preStart(): Unit = {
// create the greeter actor
val greeter = context.actorOf(Props[Greeter], "greeter")
// tell it to perform the greeting
greeter ! Greeter.Greet
}
def receive = {
// when the greeter is done, stop this actor and with it the application
case Greeter.Done => context.stop(self)
}
object Greeter {
case object Greet
case object Done
}
class Greeter extends Actor {
def receive = {
case Greeter.Greet =>
println("Hello World!")
sender ! Greeter.Done
}
}
}
object Driver {
def main(args: Array[String]) {
new HelloWorld
}
}
You need to edit your main as shown below. Secondly, in line-5, you need to change it to context.actorOf(Props(new Greeter)). This is because your Greeter does not have apply function defined, hence you need to manually create Greeter object yourself.
Working code below:
import akka.actor.ActorSystem
class HelloWorld extends Actor {
override def preStart(): Unit = {
// create the greeter actor
val greeter = context.actorOf(Props(new Greeter), "greeter")//line 5
// tell it to perform the greeting
greeter ! Greeter.Greet
}
def receive = {
// when the greeter is done, stop this actor and with it the application
case Greeter.Done => context.stop(self)
}
object Greeter {
case object Greet
case object Done
}
class Greeter extends Actor {
def receive = {
case Greeter.Greet =>
println("Hello World!")
sender ! Greeter.Done
}
}
}
object Driver {
def main(args: Array[String]) {
val system = ActorSystem("Main")
val ac = system.actorOf(Props[HelloWorld])
}
}
If you want to use your main class do the following:
import akka.actor.{ActorSystem, Props}
object Driver extends App {
val system = ActorSystem("System")
val hw = system.actorOf(Props[HelloWorld], name = "hw")
}
Which will create a new actor system and then create the HelloWorld actor using that actor system.
You can also follow the akka instructions:
set Akka.Main as the main class and give the program "com.example.HelloWorld" as argument.
val greeter = context.actorOf(Props(new Greeter), "greeter")//line 5
I don't think you need to have a new keyword there for Greeter. I believe the Props does that for you already. If anything having a new should be a
I'd like some help sorting out this scenario. I have an Akka actor where I want to inject a dependency, in this case RemoteFetcher, which I would also like mock in my tests. Like so:
main/src/scala/mypackage/Services.scala
package mypackage
import RemoteFetcherFileSystem._
trait RemoteFetcher {
def fetch( path:String ): Future[Stream[String]]
}
class MyRemoteResourceActor extends Actor with ActorLogging {
def fetchRemote( path:String ) = implicitly[RemoteFetcher].fetch( path )
def receive = {
case FetchRemoteResource( path ) => fetchRemote( path ).map( _.foreach( sender ! _ ) )
}
}
For this to work I have an implicit object that I import into the file above. Would look something like this:
implicit object RemoteFetcherFileSystem extends RemoteFetcher {
def fetchRemote( path:String ) = Future[Stream[String]] { ... reading from file system ... }
}
Now in my tests I have TestActor from the akka-testkit. Here I want to instead import my mock dependency:
implicit object RemoteFetcherMock extends RemoteFetcher {
def fetchRemote( path:String ) = Future[Stream[String]] { ... mock implementation ... }
}
My problem is that to compile Services.scala I need to import the implicit object. But how do I go about to shadow/override this in my test-files. The reason I'm not using implicit arguments is that I want to avoid having to modify all my actors constructor arguments.
I when looking around and reading up on the type class dependency injection pattern and I get it to work according to the tutorials, but I don't get it to work when I want to test and override like in my example.
I'm not sure how to do it with implicits, but typically one could inject instead like so:
trait RemoteFetcherComponent {
def remoteFetcher: RemoteFetcher
trait RemoteFetcher {
def fetch(path: String): Future[Stream[String]]
}
}
trait RemoteFetcherFileSystemComponent extends RemoteFetcherComponent {
val remoteFetcher = RemoteFetcherFileSystem
object RemoteFetcherFileSystem extends RemoteFetcher {
def fetch(path: String): Future[Stream[String]] = ???
}
}
class MyRemoteResourceActor extends Actor with ActorLogging with RemoteFetcherFileSystemComponent {
def fetchRemote(path: String) = remoteFetcher.fetch(path)
def receive = {
case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
}
}
val myRemoteResourceActor = new MyRemoteResourceActor()
And then a test value would be defined like so:
trait RemoteFetcherMockComponent extends RemoteFetcherComponent {
def remoteFetcher = RemoteFetcherMock
object RemoteFetcherMock extends RemoteFetcher {
def fetch(path: String): Future[Stream[String]] = ???
}
}
val myMockedResourceActor = new MyRemoteResourceActor with RemoteFetcherMockComponent {
override val remoteFetcher = super[RemoteFetcherMockComponent].remoteFetcher
}
The reason you are having an issue with implicits is because the way you're using it is no different from simply using def fetchRemote(path: String) = RemoteFetcherFileSystem.fetch(path). With the import, you've defined the implementation, rather than allowed it to be injected later.
You could also change the implicitly to an implicit parameter:
trait RemoteFetcher {
def fetch(path: String): Future[Stream[String]]
}
object RemoteFetcher {
implicit val fetcher = RemoteFetcherFileSystem
}
class MyRemoteResourceActor extends Actor with ActorLogging {
def fetchRemote(path: String)(implicit remoteFetcher: RemoteFetcher) = remoteFetcher.fetch(path)
def receive = {
case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
}
}
Then you could override the implicit that is resolved in the companion object of RemoteFetcher by simply importing RemoteFetcherMock.
See this post for more information about implicit parameter resolution precedence rules.