Testing

As before, we would like to conclude our journey by testing the implementation we came up with. Luckily, Lightbend follows the same approach to testing as it did with other libraries. There is a test kit that allows us to test services and persistent entities easily. We'll demonstrate how to test services in this section and leave testing persistent entities as an exercise for the reader.

It makes sense to start by testing the simplest service we defined—CookService. Here is a test for it, placed in the test scope of the cook-impl module:

import ch15.model.Dough
import com.lightbend.lagom.scaladsl.server.LocalServiceLocator
import com.lightbend.lagom.scaladsl.testkit.ServiceTest
import org.scalatest.{AsyncWordSpec, Matchers}
import play.api.libs.ws.ahc.AhcWSComponents

class CookServiceSpec extends AsyncWordSpec with Matchers {

"The CookService" should {
"make cookies from Dough" in ServiceTest.withServer(ServiceTest.defaultSetup) { ctx =>
new CookApplication(ctx) with LocalServiceLocator with AhcWSComponents
} { server =>
val client = server.serviceClient.implement[CookService]
client.cook.invoke(Dough(200)).map { cookies =>
cookies.count should ===(3)
}
}
}
}

Lagom provides a ServiceTest object whose purpose is to support testing of a single service. Its withServer constructor takes two parameters: an application constructor and a block of test code. It looks similar to the approach we used while testing the Akka-HTTP implementation in the previous chapter, but it behaves differently. ServiceTest actually starts the real server with the service. In our example, we mix it with LocalServiceLocator, so that we can get a service implementation from it in the test block. Here, we can invoke the service and verify that it works as expected. 

Our specification extends AsyncWordSpec, which gives us the freedom to formulate our expectations by mapping over Future returned by the service.

Testing a synchronous service such as CookService is very easy. But how about testing the asynchronous (streaming) service? We've built an example using BakerService. Here is one possible implementation of the unit test for it:

class BakerServiceSpec extends AsyncWordSpec with Matchers {

"The BakerService" should {
"bake cookies" in ServiceTest.withServer(ServiceTest.defaultSetup) { ctx =>
new BakerApplication(ctx) with LocalServiceLocator
} { server =>
val client = server.serviceClient.implement[BakerService]
implicit val as: Materializer = server.materializer
val input: Source[RawCookies, NotUsed] =
Source(List(RawCookies(10), RawCookies(10), RawCookies(10)))
.concat(Source.maybe)

client.bake.invoke(input).map { output =>
val probe = output.runWith(TestSink.probe(server.actorSystem))
probe.request(10)
probe.expectNext(ReadyCookies(12))
probe.expectNext(ReadyCookies(12))
// because the oven is not full for the 6 other
probe.cancel
succeed
}
}
}
}

The definition of the test, the test server, and the client is the same as before. The only difference is that, instead of our normal domain types we need to provide Source and get Source back. So we reach out for the Akka-Streams test kit and use our knowledge gained in Chapter 13Basics of Akka Streams to formulate the expectations against the streams. We create an input Source from List and use TestSink to confirm that the output of the service meets our expectations.

Now it's the time to enjoy the cookies!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.200.14