I'm working on a Spray API, with an Akka router to send the incoming messages on to a pool of actors for handling the logic. Now I want to write some tests for the API, but I'm struggling to find the right structure for the code. The API looks as follows at the moment:
import akka.actor.{ActorRef, ActorSystem, Props, Actor}
import akka.io.IO
import akka.routing.SmallestMailboxPool
import akka.util.Timeout
import akka.pattern.ask
import com.typesafe.config.ConfigFactory
import spray.json._
import spray.can.Http
import scala.concurrent.duration._
import spray.routing._
import spray.http._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure
object implicits{
implicit val system = ActorSystem("ApiSystem")
implicit val timeout = Timeout(5.seconds)
implicit val conf = ConfigFactory.load()
// Custom case class for parsing JSON parameter.
case class Msg(key1:String, key2:String, key3:Int)
object JsonProtocol extends DefaultJsonProtocol {
implicit val msg = jsonFormat3(Msg)
}
case class PostMsg(msg:String)
case object PostSuccess
case class PostFailure(msg:String)
}
import implicits._
object MyApi extends App {
override def main(Args: Array[String]):Unit = {
// create and start our service actor
val service = system.actorOf(Props(new MyApiActor(system)), "MyApi-service")
IO(Http) ? Http.Bind(service, interface = conf.getString("http.host"), port = conf.getInt("http.port"))
}
}
class MyApiActor(system: ActorSystem) extends Actor with MyApiService {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)
}
// this trait defines our service behavior independently from the service actor
trait MyApiService extends HttpService {
import implicits.JsonProtocol._
var actorPool = system.actorOf(SmallestMailboxPool(conf.getInt("actor-number")).props(Props(new HandlingActor(conf))), "msgRouter")
val myRoute =
path("msg") {
post {
entity(as[String]) { obj =>
try{
// if this parsing succeeds, the posted msg satisfies the preconditions set.
obj.parseJson.convertTo[Msg]
} catch {
case e: DeserializationException => {
complete(HttpResponse(status=StatusCodes.BadRequest, entity="Invalid json provided."))
}
case e: Exception => {
complete(HttpResponse(status=StatusCodes.InternalServerError, entity="Unknown internal server error."))
}
}
onComplete(actorPool ? PostMsg(obj)) {
case Success(value) => complete(HttpResponse(status = StatusCodes.OK, entity = "Pushed Msg"))
case Failure(value) => complete(HttpResponse(status = StatusCodes.InternalServerError, entity = "Handling failed."))
}
}
}
}
}
What I would like to test is the response of the API to various HTTP messages (i.e. correct calls, incorrect calls etc.). The logic in the handling actor is simply to push the message to a Kafka bus, so I would like to "mock" this behaviour (i.e. be able to test the API response if this push succeeds and also what happens when this push fails).
The thing I'm struggling with most at the moment is how to setup the test. For now, I am setting up the API using the same commands as in the main method shown, but I need to specify a different actorPool, as I don't want any messages to actually be pushed. How should I best go about achieving such tests?
I am using Scalatest, with the Akka and Spray testkit. (plus possibly mockito for mocking if necessary)