使用TestContainers進行容器Docker測試 – Emmanouil

banq發表於2020-12-24

Testcontainers是一個Java庫,支援JUnit測試,它提供了常見的資料庫,Selenium Web瀏覽器或其他可以在Docker容器中執行的輕型的一次性例項。
假設我們在本教程中使用maven:

<properties>
    <junit-jupiter.version>5.4.2</junit-jupiter.version>
    <testcontainers.version>1.15.0</testcontainers.version>
</properties>
 
<dependencies>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>testcontainers</artifactId>
        <version>${testcontainers.version}</version>
        <scope>test</scope>
    </dependency>
 
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>${testcontainers.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>


這裡將使用Hoverfly作為被測試的案例。
可以透過使用Java執行Hoverfly或在Hoverfly容器中預載入測試用例。這裡使用測試Hoverfly容器

package com.gkatzioura.hoverfly.docker;
 
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
 
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
 
@Testcontainers
public class ContainerBasedSimulation {
 
    private static final String SIMULATION_HOST_PATH = ContainerBasedSimulation.class.getClassLoader().getResource("simulation.json").getPath();
 
    @Container
    public static GenericContainer gcs = new GenericContainer("spectolabs/hoverfly")
            .withExposedPorts(8888)
            .withExposedPorts(8500)
            .withCommand("-webserver","-import","/var/hoverfly/simulation.json")
            .withClasspathResourceMapping("simulation.json","/var/hoverfly/simulation.json" ,BindMode.READ_ONLY);
 
 
    @Test
    void testHttpGet() {
        var hoverFlyHost = gcs.getHost();
        var hoverFlyPort = gcs.getMappedPort(8500);
        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://"+hoverFlyHost+":"+ hoverFlyPort +"/user"))
                .build();
        var res = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(HttpResponse::body)
                .join();
        Assertions.assertEquals("{\"username\":\"test-user\"}",res);
    }
 
}

講解如下:
Jupiter整合需要@Testcontainers批註。

@Testcontainers
public class ContainerBasedSimulation {
}

這時需要使用的容器的映象還沒有載入,這時使用GenericContainer :

@Container
public static GenericContainer gcs = new GenericContainer("spectolabs/hoverfly")


由於我們要將模擬應用載入到容器,因此需要從主機上設定模擬路徑。透過使用withClasspathResourceMapping,我們可以直接在類路徑中指定檔案,例如測試資源。

.withClasspathResourceMapping("simulation.json","/var/hoverfly/simulation.json",BindMode.READ_ONLY);

Hoverfly需要暴露模擬和管理埠,因此我們將指示Testcontainer暴露那些埠並將其對映到主機。

new GenericContainer("spectolabs/hoverfly")
            .withExposedPorts(8888)
            .withExposedPorts(8500)

我們需要在容器上放置一個模擬。透過使用withFileSystemBind,我們可以指定本地路徑和容器上的路徑。

...
.withFileSystemBind(SIMULATION_HOST_PATH,"/var/hoverfly/simulation.json" ,BindMode.READ_ONLY)
...

此外,docker映像可能還需要一些其他命令,因此我們將使用.withCommand來傳遞所需的命令。

...
.withCommand("-webserver","-import","/var/hoverfly/simulation.json")
...

從技術上講,我們可以說已經準備好連線到容器,但是在執行測試容器時,這時外部正常訪問不能透過指定用於繫結的埠訪問該容器,因為如果測試並行執行,將會發生衝突。因此,測試容器要做的就是將容器的裸露埠對映到隨機的本地埠。這樣避免了埠衝突。

@Test
void testHttpGet() {
    var hoverFlyHost = gcs.getHost();
    var hoverFlyPort = gcs.getMappedPort(8500);
    var client = HttpClient.newHttpClient();
    var request = HttpRequest.newBuilder()
            .uri(URI.create("http://"+hoverFlyHost+":"+ hoverFlyPort +"/user"))
            .build();
    var res = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body)
            .join();
    Assertions.assertEquals("{\"username\":\"test-user\"}",res);
}


使用GenericContainer.getMappedPort(8500),我們可以獲取用於與容器互動的埠。另外,getHost()也是必不可少的,因為它並不總是直接指向localhost。
最後:

docker ps 
>04a322447226        testcontainers/ryuk:0.3.0   "/app"     

 

相關文章