Contêineres de teste do Docker em testes Java

Contêineres de teste do Docker em testes Java

1. Introdução

In this tutorial, we’ll be looking at Java TestContainers library. Ele nos permite usar contêineres Docker em nossos testes. Como resultado, podemos escrever testes de integração independentes que dependem de recursos externos.

Podemos usar qualquer recurso em nossos testes que tenham uma imagem do docker. Por exemplo, existem imagens para bancos de dados, navegadores da web, servidores da web e filas de mensagens. Portanto, podemos executá-los como contêineres em nossos testes.

2. Exigências

A bibliotecaTestContainers pode ser usada com Java 8 e superior. Além disso, é compatível com a API de regras JUnit.

Primeiro, vamos definir a dependência maven para a funcionalidade central:


    org.testcontainers
    testcontainers
    1.11.4

Existem também módulos para contêineres especializados. Neste tutorial, usaremosPostgreSQL andSelenium. 

Vamos adicionar as dependências relevantes:


    org.testcontainers
    postgresql 
    1.11.4


    org.testcontainers
    selenium 
    1.11.4

Podemos encontrar as versões mais recentes emMaven Central.

Also, we need Docker to run containers. ConsulteDocker documentation para obter instruções de instalação.

Certifique-se de poder executar contêineres Docker em seu ambiente de teste.

3. Uso

Vamos configurar uma regra de contêiner genérica:

@ClassRule
public static GenericContainer simpleWebServer
 = new GenericContainer("alpine:3.2")
   .withExposedPorts(80)
   .withCommand("/bin/sh", "-c", "while true; do echo "
     + "\"HTTP/1.1 200 OK\n\nHello World!\" | nc -l -p 80; done");

Construímos uma regra de testeGenericContainer especificando um nome de imagem do docker. Em seguida, configuramos com métodos do construtor:

  • UsamoswithExposedPorts para expor uma porta do contêiner

  • withCommand define um comando de contêiner. Será executado quando o contêiner iniciar.

A regra é anotada com@ClassRule.  Como resultado, ela iniciará o contêiner do Docker antes que qualquer teste nessa classe execute. O contêiner será destruído após todos os métodos serem executados.

Se você aplicar a anotação@Rule, a regraGenericContainer iniciará um novo contêiner para cada método de teste. E irá parar o contêiner quando esse método de teste terminar.

We can use IP address and port to communicate with the process running in the container:

@Test
public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
  throws Exception {
    String address = "http://"
      + simpleWebServer.getContainerIpAddress()
      + ":" + simpleWebServer.getMappedPort(80);
    String response = simpleGetRequest(address);

    assertEquals(response, "Hello World!");
}

4. Modos de uso

Existem váriosusage modes dos recipientes de teste. Vimos um exemplo de execução de umGenericContainer.

A bibliotecaTestContainers também possui definições de regras com funcionalidade especializada. Eles são para contêineres de bancos de dados comuns como MySQL, PostgreSQL; e outros como clientes da web.

Embora possamos executá-los como contêineres genéricos, as especializações fornecem métodos de conveniência estendidos.

4.1. Bases de dados

Vamos supor que precisamos de um servidor de banco de dados para testes de integração da camada de acesso a dados. Podemos executar bancos de dados em contêineres com a ajuda da biblioteca TestContainers.

Por exemplo, disparamos um contêiner PostgreSQL com a regraPostgreSQLContainer. Então, podemos usar métodos auxiliares. These are getJdbcUrl, getUsername, getPassword for database connection:

@Rule
public PostgreSQLContainer postgresContainer = new PostgreSQLContainer();

@Test
public void whenSelectQueryExecuted_thenResulstsReturned()
  throws Exception {
    String jdbcUrl = postgresContainer.getJdbcUrl();
    String username = postgresContainer.getUsername();
    String password = postgresContainer.getPassword();
    Connection conn = DriverManager
      .getConnection(jdbcUrl, username, password);
    ResultSet resultSet =
      conn.createStatement().executeQuery("SELECT 1");
    resultSet.next();
    int result = resultSet.getInt(1);

    assertEquals(1, result);
}

Também é possível executar o PostgreSQL como um contêiner genérico. Mas seria mais difícil configurar a conexão.

4.2. Drivers da Web

Outro cenário útil é executar contêineres com navegadores da web. BrowserWebDriverContainer rule ativa a execução de contêineresChrome andFirefox indocker-selenium . Então, nós os gerenciamos comRemoteWebDriver. 

Isso é muito útil para automatizar testes de IU / Aceitação para aplicativos da web:

@Rule
public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer()
  .withCapabilities(new ChromeOptions());
@Test
public void whenNavigatedToPage_thenHeadingIsInThePage() {
    RemoteWebDriver driver = chrome.getWebDriver();
    driver.get("http://example.com");
    String heading = driver.findElement(By.xpath("/html/body/div/h1"))
      .getText();

    assertEquals("Example Domain", heading);
}

4.3. Docker Compose

Se os testes requerem serviços mais complexos, podemos especificá-los em um arquivodocker-compose:

simpleWebServer:
  image: alpine:3.2
  command: ["/bin/sh", "-c", "while true; do echo 'HTTP/1.1 200 OK\n\nHello World!' | nc -l -p 80; done"]

Então, usamos a regraDockerComposeContainer. Esta regra iniciará e executará serviços, conforme definido no arquivo de composição.

Usamos os métodosgetServiceHostegetServicePost para construir o endereço de conexão para o serviço:

@ClassRule
public static DockerComposeContainer compose =
  new DockerComposeContainer(
    new File("src/test/resources/test-compose.yml"))
      .withExposedService("simpleWebServer_1", 80);

@Test
public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
  throws Exception {

    String address = "http://" + compose.getServiceHost("simpleWebServer_1", 80) + ":" + compose.getServicePort("simpleWebServer_1", 80);
    String response = simpleGetRequest(address);

    assertEquals(response, "Hello World");
}

5. Conclusão

Vimos como poderíamos usar a bibliotecaTestContainers. Facilita o desenvolvimento e a execução de testes de integração.

UsamosGenericContainer rule para contêineres de determinadas imagens do docker. Em seguida, examinamos as regrasPostgreSQLContainer, BrowserWebDriverContainereDockerComposeContainer. Eles oferecem mais funcionalidade para casos de uso específicos.

Finalmente, os exemplos de código aqui podem ser encontradosover on GitHub.