Using Akka TestKit with Specs2 - scala

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.

Related

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))

Scala: How can I mock this function using Mockito?

I am wanting to test one of my akka actors, it uses slick to get the information from the database. In my actor I have this bit of code
CardStationPermissions.retrieveByStationID(stationID).foreach(card => {
I want to know how can I mock that function to change the output instead of relaying on whats in the database?
It's really difficult to mock things that are being called in a static way (in this case, a call on an object as opposed to an instance of a class). When you need to be able to mock and test things like this, I tend to agree with Mustafa's suggestion that creating a trait to represent the relevant methods to mock. A simple example would look as follows:
case class MyObject(id:Long)
trait MyDao{
def getData(input:String):List[MyObject] = ...
}
object MyDao extends MyDao
class MyActor extends Actor{
val myDao:MyDao = MyDao
def receive = {
case param:String => sender ! myDao.getData(param)
}
}
Here you can see that I have a trait to represent my dao methods (only 1 for this example) and then I mix that trait into a scala object as the default instantiation of that trait. When I setup my dao in my actor, I explicitly type it to the trait so that I can substitute a mock impl of that trait later.
So then if I wanted a simple test showing mocking, it could look something like this (via specs2):
class MyActorTest(_system:ActorSystem) extends TestKit(_system)
with Specification with Mockito with ImplicitSender{
def this() = this(ActorSystem("test"))
trait scoping extends Scope{
val mockDao = mock[MyDao]
val actor = TestActorRef(new MyActor{
override val myDao = mockDao
})
}
"A request to get data" should{
"pass the input to the dao and return the result to the sender" in new scoping{
mockDao.getData("foo") returns List(MyObject(1))
actor ! "foo"
expectMsg(List(MyObject(1)))
}
}
}

Why is this specs2 test using Mockito passing?

Suppose I had this interface and class:
abstract class SomeInterface{
def doSomething : Unit
}
class ClassBeingTested(interface : SomeInterface){
def doSomethingWithInterface : Unit = {
Unit
}
}
Note that the doSomethingWithInterface method does not actually do anything with the interface.
I create a test for it like this:
import org.specs2.mutable._
import org.specs2.mock._
import org.mockito.Matchers
import org.specs2.specification.Scope
trait TestEnvironment extends Scope with Mockito{
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class ClassBeingTestedSpec extends Specification{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in new TestEnvironment {
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
This test passes. Why? Am I setting it up wrong?
When I get rid of the scope:
class ClassBeingTestedSpec extends Specification with Mockito{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
The test fails as expected:
[info] x calls the doSomething method of the given interface
[error] The mock was not called as expected:
[error] Wanted but not invoked:
[error] someInterface.doSomething();
What is the difference between these two tests? Why does the first one pass when it should fail? Is this not an intended use of Scopes?
When you mix-in the Mockito trait to another trait you can create expectations like there was one(interface).doSomething. If such an expression fails it only returns a Result, it doesn't throw an Exception. It then gets lost in a Scope because it is just a "pure" value inside the body of a trait.
However if you mix-in the Mockito trait to a mutable.Specification then an exception will be thrown on a failure. This is because the mutable.Specification class specifies that there should be ThrownExpectations by mixing in that trait.
So if you want to create a trait extending both Scope you can either:
create the trait from inside the specification and not have it extend Mockito:
class MySpec extends mutable.Specification with Mockito {
trait TestEnvironment extends Scope {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
...
}
create trait and specification as you do, but mix-in org.specs2.execute.ThrownExpectations
trait TestEnvironment extends Scope with Mockito with ThrownExpectations {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class MySpec extends mutable.Specification with Mockito {
...
}

How to mix in traits with implicit vals of the same name but different types?

I have traits from two third party libraries that I'm trying to mix in to my own trait. They both define implicit vals named log.
However, they are of different types - one is an SLF4J Logger, the other is a Spray LoggingContext (which is really an Akka LoggingAdapter). In fact the second trait is from Spray, it's an HttpServer. (Not the most recent version you can find on Github which no longer has that val).
So, here's the code (library one renamed because it's proprietary, the Spray code snipped to show just relevant part):
object LibraryOneShim {
trait LibraryOne {
implicit val log: org.slf4j.Logger = ...
}
}
// https://github.com/spray/spray/blob/a996a5b6bdd830e613583fed86e87bf049fdb8c0/spray-routing/src/main/scala/spray/routing/HttpService.scala
trait HttpService extends Directives {
val log = LoggingContext.fromActorRefFactory // this is a LoggingContext/LoggingAdapter
}
trait MyTrait extends HttpService with LibraryOne {
val myRoute = ...
}
class MyActor extends Actor with MyTrait {
def receive = runRoute(myRoute)
}
This won't compile. The compiler complains:
error: overriding lazy value log in trait HttpService of type
java.lang.Object with spray.util.LoggingContext; lazy value log in
trait LibraryOne$class of type org.slf4j.Logger needs `override'
modifier trait DemoService extends HttpService with LibraryOne {
Is there any way I can mix in these two traits together?
As far as I can tell the only way is to create a CombinedLogger
class CombinedLogger(l1:Logger, l2:LoggingAdapter) extends Logger with LoggingAdapter {
// proxy methods to the correct logger
}
If both loggers were declared as def you could use it like this:
override def log = new CombinedLogger(super[LibraryOne].log, super[HttpService].log)
In this case it's tricky because they are defined as val which tell the Scala compiler they are a single value that will not change. And because of that it will not allow you to call super.log. So you would need to copy the logic of the overridden traits.
The other tricky part in this case is that you would need to proxy 50+ methods in the CombinedLogger.

Scalatest FunSuite and Akka Actors

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)