In a recent post we explored akka http, how to define routes, handle requests/responses, and deal with json marshalling/unmarshalling using spray.Once you know the syntax for defining routes and manage json, is pretty straightforward to run a local server:

withEmbeddedServer { test code }

One interesting application I found for this is to create a local embedded server for component testing: when test a serivce that does http calls to other services (the most common situation when using microservices), you can mock all the REST calls but that usually ignores many tricky details (like connection, protocol, authentication, etc. ), alternatively, you can do integration test, but normally involves either connecting to a vpn to access the test environment or start a bunch of docker images with the external services.As an intermediate solution, we can use a local akka http server that behaves like the services we depend on, avoiding mocking the http calls and the complexity of running the full serivices.The CodeLet’s create a small trait that we can add to our tests and manage the local server.First, we want a nice syntax… something like:

Although we need to pass the routes, so we’ll have

withEmbeddedServer(routes){ test code }

The bindAndHandle method that starts the server needs port and the host name, we can pick some defaults (“localhost” and 8080 maybe?) but we probably want the possibility of override them:

withEmbeddedServer("myserver", 8888, routes){ test code }

The block must be evaluated lazily and we might want to capture the output of the execution, so we’re going to assign the block a type of “=>T” (so we need T as a type parameter too)

Finally, to run akka http server we need to pass an implicit ActorSystem, an ActorMaterializer, and an ExecutionContext, so we’ll add those parameters too

So, the signature of the method will look like:

def withEmbeddedServer[T](host: String = "localhost", port: Int = 8080, routes: Route)(block: => T) (implicit system: ActorSystem, mat: ActorMaterializer, ec: ExecutionContext): T = ???

Now, the actual implementation is pretty straightforward: start the server, evaluate the block, stop the server, and return the value.

To keep things simple, we’re going to surround the block evaluation with a try-finally block:

def withEmbeddedServer[T](host: String = "localhost", port: Int = 8080, routes: Route)(block: => T) (implicit system: ActorSystem, mat: ActorMaterializer, ec: ExecutionContext): T = { val server = Http().bindAndHandle(routes, host, port) try { block } finally { server.flatMap(_.terminate(shutdownDeadline)) } }

The terminate method requires a timeout parameter, and for more flexibility, let’s define it as member value so it can be overridden if you don’t like the default value:

trait EmbeddedHttpServer { val shutdownDeadline: FiniteDuration = 10.seconds def withEmbeddedServer[T](host: String = "localhost", port: Int = 8080, routes: Route)(block: => T) (implicit system: ActorSystem, mat: ActorMaterializer, ec: ExecutionContext): T = { val server = Http().bindAndHandle(routes, host, port) try { block } finally { server.flatMap(_.terminate(shutdownDeadline)) } } }

Now you can mix the trait in your test class and have access to the “withEmbeddedServer” method, but we don’t need to mix it, we can create an object providing that method and we can import it anywhere we need it:

object EmbeddedHttpServer extends EmbeddedHttpServer

As an example, let’s simulate a service that respond with “hello” when you do a GET to /hi path and write test code that verifies that.

Our code to test will look like:

def callHiService(): Future[Seq[ByteString]] = { val url = "http://localhost:8989/hi" val result = Http().singleRequest(HttpRequest(uri = Uri(url))).futureValue val HttpResponse(StatusCodes.OK, _, entity, _) = result entity.dataBytes.runWith(Sink.seq) }

(In a real case you probably will call a Swagger generated client Api overriding the host and base path properties in your test config)