This framework aims towards reduction of boilerplate produced during development of integration tests. It contains traits which can be mixed into your suite classes providing a process running your application and an akka-http client which can be used for sending requests to the server instance.
If you are using Pekko instead of Akka, take a look at scala-itkit for Pekko.
Tag the new version (e.g. v3.0.0
) and push the tags (git push origin --tags
).
You need a public GPG key with your MOIA email and an account on https://oss.sonatype.org that can access the io.moia
group.
Add your credentials to ~/.sbt/sonatype_credential
and run
sbt:itkit> +publishSigned
Then close and release the repository.
sbt:itkit> +sonatypeRelease
Afterwards, add the release to GitHub.
You have the choice to differentiate between 3 different structural approaches for your tests:
- One server within one suite which is used by all tests.
- One dedicated server per test which uses the same server configuration.
- One dedicated server per test which uses a dedicated server configuration.
The most common case might be the one of using the same server instance for all of the tests of one suite. The minimum setup for starting a test with a server like that would look like this:
import io.moia.itkit.{OneServerPerSuite, OneClientPerSuite}
import org.scalatest.{AsyncWordSpecLike, Matchers}
class SampleProcessPerSuiteSpec extends AsyncWordSpecLike with Matchers with OneServerPerSuite with OneClientPerSuite {
// Defines package path to the main class of the process
override val mainClass = "io.your.project.Main"
// The message to wait for in order to know when the server process is ready to receive http requests.
override val mainSuccessMessage = "Application is started up."
// A regular expression which can be applied on log messages in order to communicate the port information.
override val portMessage = ".*port: ([0-9]+).*".r
// Additional System Properties (-D) to be added to the process start
override val additionalSystemProperties = Map("config.resource" -> "application.conf", "akka.loglevel" -> "DEBUG")
"Sample server per suite" should {
"show that a server is only started once per suite" when {
"first test is executed" in {
client.getRequest("/hello").flatMap { response =>
response.status.isSuccess() shouldBe true
}
}
}
}
}
OneServerPerSuite
trait delivers a process, which is started on startup of the suite. OneClientPerSuite
is providing
a client instance which is making use of the information delivered by the process in order to retrieve server's uri.
In order to make this work, there is need to exchange some information between the integration test and server process.
This framework takes advantage of log messages in order to achieve this communication flow. In this particular case the
test needs to know when the server is ready to receive http requests which is communicated through the
mainSuccessMessage
. The port is also communicated through a match of portMessage
regular expression.
import io.moia.itkit.fixture.OneServerPerTestWithClient
import org.scalatest.{Matchers, fixture}
class SampleProcessPerTestSpec extends fixture.AsyncWordSpecLike with Matchers with OneServerPerTestWithClient {
override val mainClass = "io.your.project.Main"
override val mainSuccessMessage = "Application is started up."
override val portMessage = ".*port: ([0-9]+).*".r
"Sample server per test" should {
"show that a new server is started per test using the same routes while using a get-fixture method" when {
"first test is executed" in { fixture =>
fixture.client.getRequest("/hello").flatMap { response =>
response.status.isSuccess() shouldBe true
}
}
}
}
}
import io.moia.itkit.{OneServerPerTest, ProcessProvider}
import org.scalatest.{AsyncWordSpecLike, Matchers}
object SampleProcessPerTestSpec {
val firstProcessProvider = new ProcessProvider {
override val mainClass = "io.your.first.project.Main"
override val mainSuccessMessage = "Application is running."
override val portMessage = ".*port: ([0-9]+).*".r
override val additionalSystemProperties = Map("akka.loglevel" -> "DEBUG")
}
val secondProcessProvider = new ProcessProvider {
override val mainClass = "io.your.second.project.Main"
override val mainSuccessMessage = "Application is ready."
override val portMessage = ".*port: ([0-9]+).*".r
}
}
class SampleProcessPerTestSpec extends AsyncWordSpecLike with Matchers with OneServerPerTest {
import SampleProcessPerTestSpec._
"Sample server per test" should {
"show that a new server is started per test with individual routes while using loan-fixture method `withProcess`" when {
"first test is executed" in withProcess(firstProcessProvider) { process =>
process.port shouldBe 2442
}
}
}
"Sample server per test" should {
"show that a new server is started per test with individual routes while using loan-fixture method `withProcessAndClient`" when {
"second test is executed" in withProcessAndClient(secondProcessProvider) { fixture =>
fixture.client.getRequest("/hello").flatMap { response =>
response.status.isSuccess() shouldBe true
}
}
}
}
}