how to make scalatest work with spraytestkit and HttpServiceActor - scala

I looked at spray 1.3.1 testkit documentation but could not find a proper example for what I need below:
I have this sample spray 1.3.1 service
trait MyService extends HttpServiceActor {
def receive = runRoute(routes)
val isAliveRoute = path("isalive") {
get {
complete("YES")
}
}
val routes = isAliveRoute
}
I'm trying to test it with spray test-kit but failing to do so here is my TestCase
#RunWith(classOf[JUnitRunner])
class MyServiceTest extends FlatSpec with ScalatestRouteTest with ShouldMatchers with MyService {
"The service" should "return a greeting for GET requests to the isalive" in {
Get() ~> isAliveRoute ~> check {
responseAs[String] should be("YES")
}
}
}
However I get
Error:(15, 87) illegal inheritance; superclass FlatSpec is not a
subclass of the superclass HttpServiceActor of the mixin trait
MyService class MyServiceTest extends FlatSpec with ScalatestRouteTest
with ShouldMatchers with MyService {
^
^
and:
Error:(17, 11) could not find implicit value for parameter ta:
MyServiceTest.this.TildeArrow[spray.routing.RequestContext,Unit]
Get() ~> isAliveRoute ~> check {
^
Are there ways around this?
Can I have my service extend HttpServiceActor and still be able to test it with scalatest and spray testkit? if so how? I want to continue extending HttpServiceActor makes life easier and code more compact and readable. But I would also like to test it with scalatest.
so i tried updating the code as comment said to split to trait and service as in:
https://github.com/spray/spray-template/blob/on_spray-can_1.1/src/main/scala/com/example/MyService.scala
class MyServiceActor extends Actor with MyService {
def actorRefFactory = context
def receive = runRoute(routes)
}
trait MyService extends HttpService {
val isAliveRoute = path("isalive") {
get {
complete("OK")
}
}
val routes = isAliveRoute
}
#RunWith(classOf[JUnitRunner])
class MyServiceTest extends FlatSpec with ShouldMatchers with MyService with ScalatestRouteTest {
def actorRefFactory = system
"The service" should "return a greeting for GET requests to the isalive" in {
Get() ~> isAliveRoute ~> check {
responseAs[String] should be("YES")
}
}
}
but i get:
Testing started at 13:26 ... [DEBUG] [05/14/2014 13:26:25.813]
[ScalaTest-run]
[EventStream(akka://com-server-web-conf-MyServiceTest)] logger
log1-Logging$DefaultLogger started [DEBUG] [05/14/2014 13:26:25.814]
[ScalaTest-run]
[EventStream(akka://com-server-web-conf-MyServiceTest)] Default
Loggers started Request was not handled
org.scalatest.exceptions.TestFailedException: Request was not handled
at
spray.testkit.ScalatestInterface$class.failTest(ScalatestInterface.scala:25)
at

I had similar problem with one difference. At complete statement I had sending message to another actor, so I needed actor functionality to test behavior. I solved it that way:
trait MyService extends HttpService {
val myActor: ActorRef
val homeS: ActorRef
(...)
and sending message inside get to
path("isalive") { get {
ctx: RequestContext => homeS.tell(ctx, myActor ) }
//on homeS actor:
def receive = {
case ctx: RequestContext =>
ctx.complete( ... )
but if you don't need actor functionality of in MyService then better is to do like #jrudolph said in comment.
Full code here: https://github.com/kastoestoramadus/simple_zookeeper.git

Related

How to not bind to localhost with ScalatestRouteTest

I want to test routes with ScalatestRouteTest as follows:
trait MyRoutes extends Directives {
self: Api with ExecutionContextProvider =>
val myRoutes: Route =
pathPrefix("api") {
path("") {
(get & entity(as[MyState])) {
request => {
complete(doSomething(request.operation))
}
}
}
}
}
}
class RoutesSpec extends WordSpecLike with Api with ScalatestRouteTest
with Matchers with MyRoutes with MockitoSugar {
"The Routes" should {
"return status code success" in {
Get() ~> myRoutes ~> check {
status shouldEqual StatusCodes.Success
}
}
}
}
When running the test I get the runtime error:
Could not run test MyRoutesSpec: org.jboss.netty.channel.ChannelException: Failed to bind to: /127.0.0.1:2552
I don't want to bind to the localhost. How can this be accomplished?
The solution was to disable remoting and clustering (this was enabled in a separate configuration file) and use the default provider.
The actor remoting and clustering conflicts with the running application (started for the routing test). They pick up the same configuration and therefore both try to use the same port which conflicts.
The following code was added in trait MyRoutes to make it work:
// Quick hack: use a lazy val so that actor system can "instantiate" it
// in the overridden method in ScalatestRouteTest while the constructor
// of this class has not yet been called.
lazy val routeTestConfig =
"""
| akka.actor.provider = "akka.actor.LocalActorRefProvider"
| persistence.journal.plugin = "akka.persistence.journal.inmem"
""".stripMargin
override def createActorSystem(): ActorSystem =
ActorSystem("RouteTest", ConfigFactory.parseString(routeTestConfig))

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

Spray.io: Can't compile test spec

I have the following service:
trait PingService extends MyHttpService {
val pingRoutes =
path("ping") {
get {
complete("message" -> "pong")
}
}
}
MyHttpServiceis a custom class that extends HttpServiceand only contains utility methods.
This is the test spec:
import akka.actor.ActorRefFactory
import org.json4s.{DefaultFormats, Formats}
import org.scalatest.{FreeSpec, Matchers}
import spray.testkit.ScalatestRouteTest
class PingServiceSpec extends FreeSpec with PingService with ScalatestRouteTest with Matchers {
override implicit def actorRefFactory: ActorRefFactory = system
override implicit def json4sFormats: Formats = DefaultFormats
"Ping service" - {
"when calling GET /ping" - {
"should return 'pong'" in {
Get("/ping") ~> pingRoutes ~> check {
status should equal(200)
entity.asString should contain("pong")
}
}
}
}
}
Whenever I try to run the tests, I get the following error:
could not find implicit value for parameter ta: PingServiceSpec.this.TildeArrow[spray.routing.RequestContext,Unit]
Get("/ping") ~> userRoutes ~> check {
^
Am I doing something stupid? Any kind of help will be appreciated!
EDIT: Although this might look like a dupe of this question, it's not.
The solution provided in that post it's not working.
The ScalatestRouteTest already provides an implicit ActorSystem. Remove the implicit modifier from your actorRefFactory method and the test should get executed.

Creating a TestActorRef results in NullPointerException

I am trying to get a TestActorRef like that
class NotifySenderTest(_system: ActorSystem) extends TestKit(_system) with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll with BeforeAndAfter {
def this() = this(ActorSystem("NotifySenderTest"))
override def afterAll {
TestKit.shutdownActorSystem(system)
}
"A NotifySender" must {
"be able to process the required messages" in {
val actorRef = TestActorRef[NotifySender] //Line 92
}
}
the this actor
class NotifySender extends Actor with Stash {
import Tcp._
import context.system
def receive = {
[...]
}
}
But this leaves me with the following stacktrace
java.lang.NullPointerException: at
akka.actor.dungeon.Dispatch$class.init(Dispatch.scala:62) at
akka.actor.ActorCell.init(ActorCell.scala:338) at
akka.actor.LocalActorRef.(ActorRef.scala:304) at
akka.testkit.TestActorRef.(TestActorRef.scala:21) at
akka.testkit.TestActorRef$.apply(TestActorRef.scala:141) at
akka.testkit.TestActorRef$.apply(TestActorRef.scala:137) at
akka.testkit.TestActorRef$.apply(TestActorRef.scala:146) at
akka.testkit.TestActorRef$.apply(TestActorRef.scala:144) at
actor.NotifySenderTest$$anonfun$2$$anonfun$apply$mcV$sp$4.apply$mcV$sp(NotifySenderTest.scala:92)
at
actor.NotifySenderTest$$anonfun$2$$anonfun$apply$mcV$sp$4.apply(NotifySenderTest.scala:91)
...
Edit: It seems to have something to do with this actor in particular. Getting a TestActorRef to another actor class is working correctly. I read that there was a problem with TextActorRefs for actors that have the Stash trait, but this was said to be resolved in the current version. (Reference)
Edit2: Ok. I was wrong. The current release is not 2.3. So I have to wait?!
Verified that upgrading to akka 2.3.0 is the correct answer for fixing TestActorRef with the Stash trait.
Instantiate the actor:
val actorRef = TestActorRef(new NotifySender())
That's how I always go about it anyway. :)

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 {
...
}