Service testing

HeartAI system services are developed with consideration of principles from test-driven development and behaviour-driven development. Following a microservices framework, service testing allows mocking of server and client deployments, the stubbing of services for service-to-service communication, and deployment of ephemeral backing services for high-parity integration testing. Testing abstractions are provided by Lagom and Akka Testing. Testing frameworks are provided by ScalaTest.

Deployment of service testing is integrated with the HeartAI GitHub Actions implementation, allowing the GitHub review process to deploy and refer to these testing specifications.

Deployment implementation

Further information about the HeartAI deployment implementation may be found with the following documentation sections:

Service testing deployment

The Lagom microservices framework provides mechanisms for testing server deployments corresponding to a Play application loader:

private val server = ServiceTest.startServer(
  ServiceTest.defaultSetup.withCluster()
) { ctx =>
  new HelloWorldApplication(ctx)
    with LocalServiceLocator
    with TestTopicComponents {

    override lazy val readSide: ReadSideTestDriver =
      new ReadSideTestDriver()(materializer, executionContext)
  }
}

protected override def afterAll(): Unit =
  server.stop()

Service clients are also mockable with reference to a server deployment:

private val client: HelloWorldServiceAPI =
  server.serviceClient.implement[HelloWorldServiceAPI]

Specification testing

The primary testing framework is ScalaTest.

"HelloWorldService" should {
  "implement service endpoint behaviour for pingService" in {
    client.pingService()
      .invoke()
      .map(_.msg.isDefined shouldBe true)
  }
}
private val pingMsg: PingMsg =
  PingMsg(
    msg = Some("Hello"))

"HelloWorldService" should {
  "implement service endpoint behaviour for pingServiceByPOST" in {
    client.pingServiceByPOST()
      .invoke(pingMsg)
      .map(_.msg shouldBe pingMsg.msg)
  }
}

Event-sourced behaviour is testable with the Akka Persistence TestKit:

private val testGreeting: GreetingIMPL =
  GreetingIMPL(
    msg = "Bonjour")

private val testEntity =
  EventSourcedBehaviorTestKit[HelloWorldCommand, HelloWorldEvent, HelloWorldState](
    system,
    HelloWorldEntity.create(
      PersistenceId("HelloEntity", "1")))

"HelloWorldService" should {
  "implement event-sourced behaviour for useGreeting" in {
    val result = testEntity.runCommand[StatusReply[Done]](
      UpdateGreetingMessageCommand(testGreeting.msg, _))
    result.reply shouldBe StatusReply.Success(Done)
    result.event shouldBe GreetingMessageUpdatedEvent(testGreeting.msg)
    result.state.msg shouldBe testGreeting.msg
  }
}