DEV Community

Roger Viñas Alcon for Adevinta Spain

Posted on • Updated on

Testing with WireMock Docker

In Testing with @WireMockTest I showed a demo using @WireMockTest and WireMockExtension to mock APIs in our tests.

But WireMock has an official Docker image, let's try that out too! 🤩

Diagram

GitHub logo rogervinas / wiremock-testing

🤹 WireMock Testing

Docker Compose

We configure these two containers in docker-compose.yml:

version: "3.9"

services:

  foo-api:
    image: wiremock/wiremock:2.32.0
    ports:
      - "8080"
    command:
      - "--global-response-templating"
    volumes:
      - ./wiremock/foo-api:/home/wiremock

  bar-api:
    image: wiremock/wiremock:2.32.0
    ports:
      - "8080"
    command:
      - "--global-response-templating"
    volumes:
      - ./wiremock/bar-api:/home/wiremock
Enter fullscreen mode Exit fullscreen mode
  • We use dynamic ports.
  • We enable response templating adding the parameter --global-response-templating (see command line options).
  • Directories containing WireMock mappings are mounted as volumes.

App test with Testcontainers

Static stubs

With a little help from Testcontainers JUnit5 extension we first test the static stubs already configured:

@Testcontainers
@TestInstance(PER_CLASS)
class AppShouldWithWireMockDocker {

 companion object {
  private const val name = "Ivy" 
  private const val fooServiceName = "foo-api"
  private const val fooServicePort = 8080
  private const val barServiceName = "bar-api"
  private const val barServicePort = 8080
  private lateinit var fooApiHost: String
  private var fooApiPort: Int = 0
  private lateinit var barApiHost: String
  private var barApiPort: Int = 0

  @Container
  @JvmStatic
  val container = DockerComposeContainer<Nothing>(File("docker-compose.yml"))
   .apply {
    withLocalCompose(true)
    withExposedService(fooServiceName, fooServicePort, forListeningPort())
    withExposedService(barServiceName, barServicePort, forListeningPort())
    withLogConsumer()
   }

  @BeforeAll
  @JvmStatic
  fun beforeAll() {
    fooApiHost = container.getServiceHost(fooServiceName, fooServicePort)
    fooApiPort = container.getServicePort(fooServiceName, fooServicePort)
    barApiHost = container.getServiceHost(barServiceName, barServicePort)
    barApiPort = container.getServicePort(barServiceName, barServicePort)
  }
 }

 @Test
 fun `call foo and bar`() {
  val fooApiUrl = "http://${fooApiHost}:${fooApiPort}"
  val barApiUrl = "http://${barApiHost}:${barApiPort}"

  val app = App(name, fooApiUrl, barApiUrl)

  assertThat(app.execute()).isEqualTo(
   """
    Hi! I am $name
    I called Foo and its response is Hello $name I am Foo!
    I called Bar and its response is Hello $name I am Bar!
    Bye!
   """.trimIndent()
  )
 }
}
Enter fullscreen mode Exit fullscreen mode
  • We obtain the dynamic ports that have been assigned to each container to build fooApiUrl and barApiUrl.

Dynamic stubs

We can also configure our stubs programmatically using the WireMock client and connecting it to the WireMock Admin API of the two WireMock containers:

@Test
fun `call foo an bar with dynamic stubs`() {
 val fooApiUrl = "http://${fooApiHost}:${fooApiPort}/dynamic"
 val barApiUrl = "http://${barApiHost}:${barApiPort}/dynamic"

 WireMock(fooApiHost, fooApiPort)
  .register(get(urlPathEqualTo("/dynamic/foo"))
   .withQueryParam("name", WireMock.equalTo(name))
   .willReturn(ok().withBody("Hi $name I am Foo, how are you?"))
 )
 WireMock(barApiHost, barApiPort)
  .register(get(urlPathMatching("/dynamic/bar/$name"))
   .willReturn(ok().withBody("Hi $name I am Bar, nice to meet you!"))
 )

 val app = App(name, fooApiUrl, barApiUrl)
 assertThat(app.execute()).isEqualTo(
   """
     Hi! I am $name
     I called Foo and its response is Hi $name I am Foo, how are you?
     I called Bar and its response is Hi $name I am Bar, nice to meet you!
     Bye!
   """.trimIndent()
 )
}
Enter fullscreen mode Exit fullscreen mode

App run with Docker Compose

We can easily use the same docker-compose used by the test to start the application and run/debug it locally:

Diagram

In this case we'll need to use fixed ports but we can achieve that with a docker-compose.override.yml like this:

version: "3.9"

services:

  foo-api:
    ports:
      - "8081:8080"

  bar-api:
    ports:
      - "8082:8080"
Enter fullscreen mode Exit fullscreen mode

This override is only applied when we execute docker compose manually and it conveniently does not affect @Testcontainers.

This is cool, isn't it? 😎

Ofertas Backend

Discussion (0)