Як нам допоміг Docker в написанні тестів

Щоб показувати вам рекламу — ми в GetIntent повинні бути впевнені в стійкій і безвідмовної роботи нашої рекламної платформи. Надійність системи складається з багатьох компонентів: тип використовуваного заліза, системна/мережева конфігурація, архітектура програми. Внесення змін до досить складні, розподілені додатки завжди несе в собі ризик.
Розробники зі свого боку намагаються мінімізувати ці ризики і пишуть тести: unit та інтеграційні. Написання unit тестів зазвичай не складає жодних труднощів. З інтеграційними тестами, в залежності від їх витонченості, ситуація складніша.



Коли тести використовують Tomcat або Jetty це не доставляє ніяких проблем: ці сервера написані на java і можуть легко вбудовуватися в тести. Але, наприклад, ми використовуємо Aerospike і коли ми хочемо протестувати взаємодію з базою даних нас чекають наступні труднощі:

  • Aerospike написаний не на Java і не може бути легко вбудований у наш додаток.
  • Хочеться щоб розробник міг запустити тести на всіх популярних платформах: Windows, OS X або Linux. Aerospike надає бінарники тільки для Linux.
  • Тести можуть виконуватися паралельно, отже нам потрібно кілька серверів?
  • Кожен тест повинен одержувати розпорядження чистий екземпляр бази даних.
Часто для цих цілей використовується загальне тестове оточення, наприклад віддалений сервер, на якому вже налаштований Aerospike і на якому можна запустити деякі тести. Однак, підхід володіє певними недоліками:

  • Запуск тестів на віддаленому сервері займає значно більше часу, ніж запуск локальних тестів. Це особливо помітно при роботі з повільним інтернетом.
  • Виникають проблеми з ізоляцією, у випадку, якщо декілька розробників вирішили одночасно протестувати додаток.
  • Досить часто політика інформаційної безпеки компанії вимагає ховати сервера корпоративних VPN. Перспектива налаштування VPN клієнта може віднадити всі бажання працювати з дому.
Альтернативний варіант: вимагати підготувати локальне оточення для тестів, писати довгі інструкції по установці і настройці Aerospike для трьох платформ (Win/Mac/Linux). Але є й інший варіант — скористатися утилітами автоматизації, такими, як Docker.

Docker — це система розгортання і керування додатками в ізольованому середовищі (контейнерах). Він побудований на принципах клієнт-серверної архітектури, docker client є для всіх основних OC, а docker daemon працює тільки на Linux — системах. Однак це не проблема: з допомогою docker-machine можна запустити docker і на Windows, OS X (правда на віртуальному хості). Отже, щоб запустити тести розробнику необхідно мати на машині налаштований docker client — перевірити це можна командою: docker run hello-world. Для Windows і OS X буде потрібно встановити docker-machine.

Embedded aerospike
Для використання Aerospike в інтеграційних тестах, ми написали обгортку для docker і docker-machine. Вона вміє:

  1. Стартувати / зупиняти контейнери.
  2. Змонтувати конфігураційний файл aerospike.conf всередину контейнера.
  3. Прив'язати порт контейнера до вільного порту хоста.
  4. Стартувати / зупиняти Docker Machine якщо тести запускаються на Windows/OS X.
Для управління контейнерами ми використовуємо docker-java client API — популярний Java API клієнт для Docker. Щоб запустити сервер Aerospike всередині Docker контейнера, налаштувати перенаправлення портів і змонтувати конфігураційний файл, треба виконати команду:

docker run -d -P -p 3000:3000 -v
path/to/aerospike.conf:/etc/aerospike/aerospike.conf --name aerospike aerospike

А ось код, який робить теж саме, використовуючи Docker Remote Api


ExposedPort tcp3000 = ExposedPort.tcp(3000);
Volume volume = new Volume("./etc/aerospike/aerospike.conf");
Ports portBindings = new Ports();
portBindings.bind(tcp3000, Ports.binding(aerospikePort));
CreateContainerResponse container = dockerClient.createContainerCmd(IMAGE_ID)
.withExposedPorts(tcp3000)
.withPortBindings(portBindings)
.withBinds(new Bind(aerospikeConfPath, volume, AccessMode.ro))
.exec();

dockerClient.startContainerCmd(container.getId())
.exec();

Більш детально можна подивитися в класі AerospikeServer.

Пишемо тест
Давайте розглянемо приклад інтеграційного тесту з використанням embedded-aerospike. Припустимо у нас є клас SimpleAerospikeClient, який уміє зберігати і отримувати сегменти користувача ідентифікатора.

public Set getSegments(Long userId);
public void addSegments(Long userId, Set segments);

Посмотреть повний клас.

Пишемо тест, який перевіряє коректність реалізації даних методів.

Спочатку треба налаштувати і запустити сервер.

@BeforeMethod
public void setUp() throws Exception {
aerospikeServer = AerospikeServer.builder()
.aerospikeConfPath(getClass().getResource("/aerospike.conf").getFile())
.dockerConfig(DockerClientConfig.createDefaultConfigBuilder().build())
.build();
aerospikeServer.start();
}

Перевіряємо, що дані правильно записуються і читаються з бази даних.

@Test
public void test() {
long userId = ThreadLocalRandom.current().nextLong();
aerospikeClient.addSegments(userId, new HashSet<Integer>() {{
add(150);
add(151);
}});
Set<Integer> segments = aerospikeClient.getSegments(userId);
Assert.assertEquals(segments.size(), 2);
Assert.assertTrue(segments.contains(150));
Assert.assertTrue(segments.contains(151));
}

Не забуваємо зупинити і видалити контейнери, які були створені під час тестів.

@AfterMethod
public void tearDown() throws Exception {
aerospikeServer.stop();
}

Висновок
Такий підхід дозволяє запускати інтеграційні тести на машині розробника, а це означає, що по-перше, вам не треба витрачати ресурси на підтримку тестових серверів, а по-друге ви можете розробляти і тестувати програму без доступу до інфраструктури. Ми розглянули приклад роботи з Aerospike, але очевидно що таким чином можна тестувати взаємодія вашої програми з будь-якими сервісами.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.