Иногда, когда вы запускаете свои тесты в своей среде CI, вы хотите запускать тесты параллельно. Этот параллелизм программируется в инструменте сборки, таком как Maven или Gradle, или с помощью плагина Jenkins .
Если вы используете Docker в качестве инструмента тестирования для предоставления внешних зависимостей приложению (например, баз данных, почтовых серверов, FTP-серверов,…), вы можете столкнуться с большой проблемой, и это, вероятно,
Docker Hos t используется один и при параллельном запуске тестов все они попытаются запустить контейнер с тем же именем. Поэтому, когда вы запускаете второй тест (параллельно), вы получите ошибку, связанную с именем контейнера конфликта, из-за попытки запустить на одном и том же Docker Host два контейнера с одинаковым именем или имеющие одинаковый порт привязки в двух контейнерах.
Итак, достигнув этой точки, вы можете сделать две вещи:
- Вы можете иметь один Docker Host для каждого параллельного теста.
- Вы можете повторно использовать тот же Docker Host и использовать Arquillian Cube Star Operator .
Arquillian Cube — это расширение Arquillian, которое можно использовать для управления контейнерами Docker в ваших тестах.
Чтобы использовать Arquillian Cube, вам нужен демон Docker , работающий на компьютере (он может быть локальным или нет), но, вероятно, он будет локальным.
Arquillian Cube предлагает три различных способа определения контейнера (ов):
- Определение файла docker-compose .
- Определение объекта-контейнера.
- Использование контейнерного объекта DSL.
В этом примере я собираюсь показать вам, как использовать docker-compose и Container Object DSL .
Звездный оператор позволяет указать 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
|
plugins { id "io.spring.dependency-management" version "1.0.2.RELEASE" } apply plugin: 'java' repositories { mavenCentral() jcenter() } dependencyManagement { imports { mavenBom 'org.jboss.arquillian:arquillian-bom:1.1.13.Final' } } dependencies { testCompile 'junit:junit:4.12' testCompile 'org.jboss.arquillian.junit:arquillian-junit-standalone' testCompile 'org.arquillian.cube:arquillian-cube-docker:1.3.2' } test { maxParallelForks = 2 testLogging.showStandardStreams = true } |
1
2
3
4
5
6
|
#src/test/docker/docker-compose.yml redis*: image: redis:3.0.7 ports: - "6379" |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@RunWith (Arquillian. class ) public class TestOne { @HostPort (containerName = "redis*" , value = 6379 ) private int portBinding; @Test public void should_print_port_binding() throws InterruptedException { System.out.println(TestOne. class .getCanonicalName() + " - " + portBinding); TimeUnit.SECONDS.sleep( 4 ); } } |
Вы можете увидеть в файле docker-compose.yml важное изменение в типичном файле docker-compose , и оно заключается в том, что имя заканчивается оператором star (*) [ redis * ]. Вот как вы указываете Arquillian Cube, что это имя должно генерироваться динамически для каждого выполнения.
Тогда есть три теста (здесь только показали первый), что все они выглядят одинаково. В основном он печатает на консоли порт привязки для подключения к серверу.
Наконец, есть файл build.gradle , который выполняет два теста параллельно. Поэтому, если вы запустите тесты в Gradle ( ./gradlew test ), вы увидите, что два теста выполняются одновременно, и когда он завершит один из них, будет выполнен оставшийся тест. Затем, если вы проверите вывод, вы увидите следующий вывод:
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
45
46
47
48
49
50
51
52
53
54
|
org.superbiz.parallel.runner.TestOne STANDARD_OUT CubeDockerConfiguration: serverUri = tcp: //192 .168.99.100:2376 machineName = dev certPath = /Users/alex/ .docker /machine/machines/dev tlsVerify = true dockerServerIp = 192.168.99.100 definitionFormat = COMPOSE clean = false removeVolumes = true dockerContainers = containers: redis_9efae4a8-fcb5-4f9e-9b1d-ab591a5c4d5a: alwaysPull: false image: redis:3.0.7 killContainer: false manual: false portBindings: !! set {56697->6379 /tcp : null} readonlyRootfs: false removeVolumes: true networks: {} org.superbiz.parallel.runner.TestThree STANDARD_OUT CubeDockerConfiguration: serverUri = tcp: //192 .168.99.100:2376 machineName = dev certPath = /Users/alex/ .docker /machine/machines/dev tlsVerify = true dockerServerIp = 192.168.99.100 definitionFormat = COMPOSE clean = false removeVolumes = true dockerContainers = containers: redis_88ff4b81-80cc-43b3-8bbe-8638dd731d8e: alwaysPull: false image: redis:3.0.7 killContainer: false manual: false portBindings: !! set {56261->6379 /tcp : null} readonlyRootfs: false removeVolumes: true networks: {} // ...... org.superbiz.parallel.runner.TestThree > should_print_port_binding STANDARD_OUT org.superbiz.parallel.runner.TestOne - 56261 org.superbiz.parallel.runner.TestOne > should_print_port_binding STANDARD_OUT org.superbiz.parallel.runner.TestOne - 56697 org.superbiz.parallel.runner.TestTwo > should_print_port_binding STANDARD_OUT org.superbiz.parallel.runner.TestOne - 56697 |
Таким образом, как вы можете видеть в журнале, имя контейнера не redis или redis * , а redis, за которым следует UUID . Также вы можете видеть, что при выводе на печать порт привязки отличается в каждом случае.
Но если вы не хотите использовать подход docker-compose , вы также можете определить контейнер программно, используя объект-контейнер DSL, который также поддерживает оператор звезды . В этом случае это выглядит так:
1
2
3
4
|
@ClassRule public static ContainerDslRule redisStar = new ContainerDslRule( "redis:3.2.6" , "redis*" ) .withPortBinding( 6379 ); |
Подход тот же, но с использованием объектов-контейнеров (вам нужен Arquillian Cube 1.4.0 для запуска его с объектами-контейнерами).
Обратите внимание, что благодаря этой функции вы можете запускать тесты с любой степенью параллельного выполнения, поскольку Arquillian Cube решает проблемы с именами или привязкой портов. Обратите внимание, что в случае связи между контейнерами вам все равно нужно использовать оператор «звезда», и он будет решен во время выполнения.
Чтобы узнать больше о звездном операторе, просто проверьте http://arquillian.org/arquillian-cube/#_parallel_execution
Исходный код: https://github.com/lordofthejars/parallel-docker
Ссылка: | Проведение параллельных тестов в Docker от нашего партнера JCG Алекса Сото в блоге One Jar To Rule All . |