0

I want to start testcontainers from a docker-compose file (postgres and kafka instance), before the play application (with slick) starts up. I want this, so I can write a end to end test. I can not seem to figure out how this is possible with Play.

import java.io.File
import com.dimafeng.testcontainers.DockerComposeContainer.ComposeFile
import com.dimafeng.testcontainers.{DockerComposeContainer, ForAllTestContainer}
import com.typesafe.config.ConfigFactory
import org.scalatest.{BeforeAndAfterAll, FunSpec}
import org.scalatestplus.play.guice.GuiceFakeApplicationFactory
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.{Application, Configuration, Environment, Mode}

trait TestFunSpec extends FunSpec with BeforeAndAfterAll with GuiceFakeApplicationFactory {

  override def fakeApplication(): Application = new GuiceApplicationBuilder()
    .in(Environment(new File("."), getClass.getClassLoader, Mode.Test))
    .loadConfig(_ => Configuration(ConfigFactory.load("test.conf")))
    .build

}

class TestIntegrationSpec extends TestFunSpec with ForAllTestContainer {

  override val container = DockerComposeContainer(ComposeFile(Left(new File("docker-compose.yml"))))

  it("should test something") {

    assert(true)

  }
}

Scala version 2.12.10 Testcontainer version 0.35.0 Play slick version 5.0.0

When I execute the test without "TestFunSpec", the docker-compose spins up my services correctly. When I add the play application "TestFunSpec" in scope, the application tries to startup, when doing so, it tries to connect with postgres, which is not yet existing (as the testcontainers are started afterwards).

Thnx in advance.

Update: see answer section for an elaborate answer.

ielkhalloufi
  • 652
  • 1
  • 10
  • 27

1 Answers1

0

After some deep dive in the Play test suite mechanism, I came up with a workable setup.

Step 1, define your test container suite with an AppProvider:

import com.dimafeng.testcontainers.{Container, ForAllTestContainer}
import org.scalatest.Suite
import org.scalatestplus.play.AppProvider

trait PlayTestContainer extends Suite with AppProvider with ForAllTestContainer {

  override val container: Container

}

Step 2, create an abstract PlayTestContainerIntegrationSpec which extends the above trait:

import java.io.File
import com.typesafe.config.ConfigFactory
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, TestData}
import org.scalatestplus.play.PlaySpec
import org.scalatestplus.play.guice.GuiceOneAppPerTest
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.{Application, Configuration, Environment, Mode}

abstract class PlayTestContainerIntegrationSpec
    extends PlaySpec
    with PlayTestContainer
    with GuiceOneAppPerTest
    with ScalaFutures
    with IntegrationPatience
    with BeforeAndAfterEach
    with BeforeAndAfterAll {

  override def newAppForTest(testData: TestData): Application = application()

  def application(): Application =
    new GuiceApplicationBuilder()
      .in(Environment(new File("."), getClass.getClassLoader, Mode.Test))
      .loadConfig(_ => Configuration(ConfigFactory.load("test.conf")))
      .build
}

As you can see, we include the "PlayTestContainer" trait and we override the "newAppForTest" function for the construction of a play application.

Step 3, create a specific integration test, extend the above abstract PlayTestContainerIntegrationSpec, and override the container, to your specific need:

class TestIntegrationSpec extends PlayTestContainerIntegrationSpec {

  override val container = DockerComposeContainer(ComposeFile(Left(new File("docker-compose.yml"))))

  "should test something" in {

    assert(true === true)

  }
}

Hope this helps.

ielkhalloufi
  • 652
  • 1
  • 10
  • 27