Scalatest FunSuite and Akka Actors - scala

I want to write a ScalaTest test suite that uses Akka actors and runs from sbt. When I try to do this:
class Tests extends FunSuite with BeforeAndAfterAll {
override protected def beforeAll() {
class Actor1 extends Actor {
protected def receive = {
case 1 => println("One")
}
}
val sys = ActorSystem("my")
val a = sys.actorOf(Props[Actor1], "plain_actor")
a ! 1
sys.shutdown()
}
}
and then sbt test, I get
[ERROR] [01/22/2012 12:49:50.329] [default-dispatcher10] [akka://my/user/plain_actor] error while creating actor
But when I write the same code in a usual main class instead of a FunSuite, and run it by sbt run, all works. What is the difference between these two cases and how do I get Akka actors run correctly in a test suite?

If you use Prop[X] then X needs to be instantiable using newInstance, which it isn't if you declare it internally in the method.
Define the Actor class either in a package or in an object or use Props(new Actor1)

Related

Accessing Play configuration from Akka Actor

I have an Akka Actor in my Play app that accesses Play's configuration using a now deprecated method.
class MyActor (supervisor: ActorRef) extends Actor {
val loc = Play.current.configuration.getString("my.location").get
def receive = { case _ => }
}
if I do this:
import javax.inject._
class MyActor #Inject(configuration: play.api.Configuration) (supervisor: ActorRef) extends Actor {
My class won't compile and the compler returns: "classfile annotation arguments have to be supplied as named arguments"
I assume you can only DI the configuration within a controller class. So, is it possible to access the configuration from within an Akka Actore within a Play app? I could pass the configuration to the actor during construction or just have a separate config file for the actors, but both seem pretty hacky. Is there a preferred method using the Play api?
Thanks!
The answer by mana above points out the most elegant way to use DI in combination with actors in Play, but within any Actor you can find the configuration like:
context.system.settings.config
This is working in my project:
Module.scala:
class ExampleModule extends AbstractModule with AkkaGuiceSupport {
override def configure(): Unit = {
bindActor[ExampleActor]("example-actor-name")
}
}
Actor.scala:
object ExampleActor {
def props = Props[ExampleActor]
}
#Singleton
class ExampleActor #Inject()(/*some DI*/) extends Actor {
...
}
And you can then even inject that very actor into other Classes (the #Named() is optional if you have only one Actor configured) via DI:
SomeOtherClass.scala
#Singleton
class SomeOtherClass #Inject()(#Named("example-actor-name") exampleActor: ActorRef) {
...
}

Spark Unit Testing: How to initialize sc only once for all the Suites using FunSuite

I want to write spark unit test cases and I am using FunSuite for it.
But i want that my sparkContext is initialized only once , used by all the Suites and then is killed when all Suites completes.
abstract class baseClass extends FunSuite with BeforeAndAfter{
before {
println("initialize spark context")
}
after {
println("kill spark context")
}
}
#RunWith(classOf[JUnitRunner])
class A extends baseClass{
test("for class A"){
//assert
}
#RunWith(classOf[JUnitRunner])
class B extends baseClass{
test(for class b){
//assert
}
}
but when i run sbt test
I can see println statement baseClass has been called from both the tests. Obsiously When the object is created for both the classes A and B , Abstract
baseclass is called.
But then how can we achieve my purpose i.e spark context is iniliazed only once while all the test cases are run
Option 1: Use the excellent https://github.com/holdenk/spark-testing-base library that does exactly that (and provides many other nice treats). After following the readme, it's as simle as mixing-in SharedSparkContext instead of your baseClass, and you'll have an sc: SparkContext value ready to use in your test
Option 2: to do it yourself, you'd want to mix-in BeforeAndAfterAll and not BeforeAndAfter, and implement beforeAll and afterAll, which is exactly what the above-mentioned SharedSparkContext does.
I strongly recommend using the spark-testing-base library in order to manage the lifecycle of a sparkContext or sparkSession during your tests.
You won't have to pollute your tests by overriding the beforeAll, afterAll methods and managing the lifecycle of the sparkSession/sparkContext.
You can share one sparkSession/sparkContext for all the tests by overriding the following method :
def reuseContextIfPossible: Boolean = true
for more details : https://github.com/holdenk/spark-testing-base/wiki/SharedSparkContext
I hope it helps!
If you really want to share the context between suites - you'll have to make it static. Then you can use a lazy value to make it start on first use. As for shutting it down - you can leave it to the automatic Shutdown hook created each time a context is created.
It would look something like:
abstract class SparkSuiteBase extends FunSuite {
lazy val sparkContext = SparkSuiteBase.sparkContext
}
// putting the Spark Context inside an object allows reusing it between tests
object SparkSuiteBase {
private lazy val sparkContext = ??? // create the context here
}

How to mock using external call in Akka Actor using ScalaTest

I am new to entire ecosystem including Scala, Akka and ScalaTest
I am working on a problem where my Actor gives call to external system.
case object LogProcessRequest
class LProcessor extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
LogReaderDisruptor main(Array())
}
}
The LogReaderDisruptor main(Array()) is a Java class that does many other things.
The test I have currently looks like
class LProcessorSpec extends UnitTestSpec("testSystem") {
"A mocked log processor" should {
"be called" in {
val logProcessorActor = system.actorOf(Props[LProcessor])
logProcessorActor ! LogProcessRequest
}
}
}
where UnitTestSpec looks like (and inspired from here)
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.matchers.MustMatchers
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
abstract class UnitTestSpec(name: String)
extends TestKit(ActorSystem(name))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll() {
system.shutdown()
}
}
Question
How can I mock the call to LogReaderDisruptor main(Array()) and verify that it was called?
I am coming from Java, JUnit, Mockito land and something that I would have done here would be
doNothing().when(logReaderDisruptor).main(Matchers.<String>anyVararg())
verify(logReaderDisruptor, times(1)).main(Matchers.<String>anyVararg())
I am not sure how to translate that with ScalaTest here.
Also, This code may not be idiomatic, since I am very new and learning
There are a few ways to do this. The kind of OO way is to wrap logDisrupter as an object and pass it in. I would set up a companion object to instantiate the actor. Something like below. Then you can pass alternate implementation. You can also achieve a similar approach by using traits and mixing in an alternative logDisrupter only as needed.
object LProcessor {
def props(logDisrupter : LogDisrupter) = Props(new LProcessor(logDisrupter))
}
class LProcessor(logDisrupter : LogDisrupter) extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
logDisrupter.doSomething();
}
}
Then instatiate as
val logProcessorActor = system.actorOf(LProcessor.props(logDisrupter))

How to test a public method in an akka actor?

I have an akka actor:
class MyActor extends Actor {
def recieve { ... }
def getCount(id: String): Int = {
//do a lot of stuff
proccess(id)
//do more stuff and return
}
}
I am trying to create a unit test for the getCount method:
it should "count" in {
val system = ActorSystem("Test")
val myActor = system.actorOf(Props(classOf[MyActor]), "MyActor")
myActor.asInstanceOf[MyActor].getCount("1522021") should be >= (28000)
}
But it is not working:
java.lang.ClassCastException: akka.actor.RepointableActorRef cannot be cast to com.playax.MyActor
How could I test this method?
Do something like this:
import org.scalatest._
import akka.actor.ActorSystem
import akka.testkit.TestActorRef
import akka.testkit.TestKit
class YourTestClassTest extends TestKit(ActorSystem("Testsystem")) with FlatSpecLike with Matchers {
it should "count plays" in {
val actorRef = TestActorRef(new MyActor)
val actor = actorRef.underlyingActor
actor.getCount("1522021") should be >= (28000)
}
}
I generally recommend factoring any "business logic" that is executed by an Actor into a separate class that is supplied as a constructor parameter or provided via a Cake component. Doing this simplifies the Actor, leaving it only the responsibility to protect long-lived mutable state and handle incoming messages. It also facilitates testing both the business logic (by making it separately available for unit tests) and how the Actor interacts with that logic by supplying a mock / spy instance or component when testing the Actor itself.

Using Akka TestKit with Specs2

I'm trying to craft a specs2 test using Akka's TestKit. I'm stuck on a persistent compile error I can't figure out how to resolve, and I'd appreciate suggestions.
The compile error is:
TaskSpec.scala:40: parents of traits may not have parameters
[error] with akka.testkit.TestKit( ActorSystem( "testsystem", ConfigFactory.parseString( TaskSpec.config ) ) )
Following suggestions from Akka docs and internet xebia and Akka in Action, I'm trying to incorporate the TestKit into a specs2 Scope. Here's a snippet of the code where I'm getting the error:
class TaskSpec
extends Specification
with AsyncTest
with NoTimeConversions {
sequential
trait scope
extends Scope
with TestKit( ActorSystem( "testsystem", ConfigFactory.parseString( TaskSpec.config ) ) )
with AkkaTestSupport {
...
I have the following helper:
trait AkkaTestSupport extends After { outer: TestKit =>
override protected def after: Unit = {
system.shutdown()
super.after
}
}
Here is one thing you can do:
import org.specs2.mutable.SpecificationLike
import org.specs2.specification._
class TestSpec extends Actors { isolated
"test1" >> ok
"test2" >> ok
}
abstract class Actors extends
TestKit(ActorSystem("testsystem", ConfigFactory.parseString(TaskSpec.config)))
with SpecificationLike with AfterExample {
override def map(fs: =>Fragments) = super.map(fs) ^ step(system.shutdown, global = true)
def after = system.shutdown
}
This should avoid the compilation error you had because TestKit is an abstract class and it is only mixing-in traits: SpecificationLike is a trait (Specification isn't) and AfterExample is a trait.
Also the specification above runs in the isolated mode, meaning that there is a brand new TestSpec object instantiated for each example and the AfterExample trait makes sure that the system is shutdown after each example.
Finally the map method is overriden with a special step to make sure that the system created for the first TestSpec instance (the one declaring all the examples) will be cleanly disposed of.