Arquillian Cube — это расширение Arquillian, которое можно использовать для управления контейнерами Docker из Arquillian.
С этим расширением вы можете запустить Docker- контейнер (ы), выполнить тесты Arquillian и после этого закрыть контейнер (ы).
Первое, что вам нужно сделать, это добавить зависимость Arquillian Cube. Это можно сделать, используя подход Arquillian Universe:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
< dependencyManagement > < dependencies > < dependency > < groupId >org.arquillian</ groupId > < artifactId >arquillian-universe</ artifactId > < version >${version.arquillian_universe}</ version > < scope >import</ scope > < type >pom</ type > </ dependency > </ dependencies > </ dependencyManagement > < dependencies > < dependency > < groupId >org.arquillian.universe</ groupId > < artifactId >arquillian-junit-standalone</ artifactId > < scope >test</ scope > < type >pom</ type > </ dependency > < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < version >${version.junit}</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.arquillian.universe</ groupId > < artifactId >arquillian-cube-docker</ artifactId > < scope >test</ scope > < type >pom</ type > </ dependency > </ dependencies > |
Затем у вас есть три способа определения контейнеров, которые вы хотите запустить.
Первый подход — использование формата docker-compose . Вам нужно только определить файл docker-compose, необходимый для ваших тестов, и Arquillian Cube автоматически прочитает его, запустит все контейнеры, выполнит тесты и, наконец, после этого они остановят и удалят их.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
version: '2' services: pingpong: image: jonmorehouse /ping-pong ports: - "8080:8080" networks: app_net: ipv4_address: 172.16.238.10 ipv6_address: 2001:3984:3989::10 front: back: networks: front: driver: bridge back: driver: bridge app_net: driver: bridge driver_opts: com.docker.network.enable_ipv6: "true" ipam: driver: default config: - subnet: 172.16.238.0 /24 gateway: 172.16.238.1 - subnet: 2001:3984:3989:: /64 gateway: 2001:3984:3989::1 |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@RunWith (Arquillian. class ) public class PingPongTest { @HostIp String ip; @HostPort (containerName = "pingpong" , value = 8080 ) int port; @Test public void should_execute_a_ping_pong() { // ... } } |
В предыдущем примере определен файл составления Docker версии 2 (его можно сохранить в корне проекта, или в src / {main, test} / docker, или в src / {main, test} / resources, и Arquillian Cube выберет автоматически), создает определенную сеть и запускает определенный контейнер службы, выполняет данный тест. и, наконец, останавливается и удаляет сеть и контейнер. Ключевым моментом здесь является то, что это происходит автоматически, вам не нужно ничего делать вручную.
Второй подход заключается в использовании шаблона объекта контейнера . Вы можете рассматривать объект- контейнер как механизм для инкапсуляции областей (данных и действий), связанных с контейнером, с которым ваш тест может взаимодействовать. В этом случае не требуется docker-compose .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@RunWith (Arquillian. class ) public class FtpClientTest { public static final String REMOTE_FILENAME = "a.txt" ; @Cube FtpContainer ftpContainer; @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void should_upload_file_to_ftp_server() throws Exception { // Given final File file = folder.newFile(REMOTE_FILENAME); Files.write(file.toPath(), "Hello World" .getBytes()); // When FtpClient ftpClient = new FtpClient(ftpContainer.getIp(), ftpContainer.getBindPort(), ftpContainer.getUsername(), ftpContainer.getPassword()); try { ftpClient.uploadFile(file, REMOTE_FILENAME, "." ); } finally { ftpClient.disconnect(); } // Then final boolean filePresentInContainer = ftpContainer.isFilePresentInContainer(REMOTE_FILENAME); assertThat(filePresentInContainer, is( true )); } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@Cube (value = "ftp" , portBinding = FtpContainer.BIND_PORT + "->21/tcp" ) @Image ( "andrewvos/docker-proftpd" ) @Environment (key = "USERNAME" , value = FtpContainer.USERNAME) @Environment (key = "PASSWORD" , value = FtpContainer.PASSWORD) public class FtpContainer { static final String USERNAME = "alex" ; static final String PASSWORD = "aixa" ; static final int BIND_PORT = 2121 ; @ArquillianResource DockerClient dockerClient; @HostIp String ip; public String getIp() { return ip; } public String getUsername() { return USERNAME; } public String getPassword() { return PASSWORD; } public int getBindPort() { return BIND_PORT; } public boolean isFilePresentInContainer(String filename) { try ( final InputStream file = dockerClient.copyArchiveFromContainerCmd( "ftp" , "/ftp/" + filename).exec()) { return file != null ; } catch (Exception e) { return false ; } } } |
В этом случае вы используете аннотации, чтобы определить, как должен выглядеть контейнер. Также, поскольку вы используете объекты Java, вы можете добавить методы, которые инкапсулируют операции с самим контейнером, как в этом объекте, где операция проверки, был ли загружен файл, была добавлена в объект контейнера.
Наконец, в вашем тесте вам нужно только аннотировать его аннотацией @Cube .
Обратите внимание, что вы даже можете создать определение контейнера программно:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Cube (value = "pingpong" , portBinding = "5000->8080/tcp" ) public class PingPongContainer { @HostIp String dockerHost; @HostPort ( 8080 ) private int port; @CubeDockerFile public static Archive<?> createContainer() { String dockerDescriptor = Descriptors.create(DockerDescriptor. class ) .from( "jonmorehouse/ping-pong" ) .expose( 8080 ) .exportAsString(); return ShrinkWrap.create(GenericArchive. class ) .add( new StringAsset(dockerDescriptor), "Dockerfile" ); } public int getConnectionPort() { return port; } public String getDockerHost() { return this .dockerHost; } } |
В этом случае файл Dockerfile создается программным способом в объекте контейнера и используется для сборки и запуска контейнера.
Третий способ — использование объекта контейнера DSL. Этот подход позволяет избежать создания класса объекта-контейнера и использования аннотаций для его определения. Он может быть создан с использованием DSL, предоставленного для этой цели:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@RunWith (Arquillian. class ) public class PingPongTest { @DockerContainer Container pingpong = Container.withContainerName( "pingpong" ) .fromImage( "jonmorehouse/ping-pong" ) .withPortBinding( 8080 ) .build(); @Test public void should_return_ok_as_pong() throws IOException { String response = ping(pingpong.getIpAddress(), pingpong.getBindPort( 8080 )); assertThat(response).containsSequence( "OK" ); } } |
В этом случае подход очень похож на предыдущий, но вы используете DSL для определения контейнера.
У вас есть три способа, первый — стандартный, следующий за соглашениями о создании docker, другие могут быть использованы для определения повторно используемых частей для ваших тестов.
Вы можете прочитать больше об Arquillian Cube на http://arquillian.org/arquillian-cube/
Мы продолжаем учиться,
Alex
И ты думал, что этот дурак никогда не сможет победить? Хорошо, посмотри на меня, я возвращаюсь снова, я почувствовал вкус любви простым способом, И если тебе нужно знать, пока я все еще стою, ты просто исчезнешь ( Я все еще стою — Элтон Джон)
Музыка: https://www.youtube.com/watch?v=ZHwVBirqD2s
Ссылка: | 3 способа использования Docker Containers для тестирования в Arquillian от нашего партнера по JCG Алекса Сото в блоге One Jar To Rule All . |