Slide 1

Slide 1 text

Тесты на Codeception для PHP-бэкендов Сташевский Павел QA engineer, Lamoda

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Зачем нам нужны авто-тесты?

Slide 4

Slide 4 text

Зачем нам нужны авто-тесты? 1. Не тестировать регресс руками

Slide 5

Slide 5 text

Зачем нам нужны авто-тесты? 1. Не тестировать регресс руками 2. Быстро релизить

Slide 6

Slide 6 text

Зачем нам нужны авто-тесты? 1. Не тестировать регресс руками 2. Быстро релизить 3. Использовать в качестве документации

Slide 7

Slide 7 text

Зачем нам нужны авто-тесты? 1. Не тестировать регресс руками 2. Быстро релизить 3. Использовать в качестве документации 4. Ускорить onboarding новых сотрудников

Slide 8

Slide 8 text

Какие нам нужны авто-тесты? Unit tests Integration tests System tests E2E tests

Slide 9

Slide 9 text

Какие нам нужны авто-тесты? Unit tests Integration tests System tests E2E tests API: REST, SOAP. Тесты на взаимодействия с другими системами, внутреннюю логику.

Slide 10

Slide 10 text

Какие нам нужны авто-тесты? Unit tests Integration tests System tests E2E tests API: REST, SOAP. Тесты на взаимодействия с другими системами, внутреннюю логику. Flow-тесты - тесты на бизнес-флоу для объектов системы (например, заказа). UI-backend тесты.

Slide 11

Slide 11 text

Какие нам нужны авто-тесты? Unit tests Integration tests System tests E2E tests API: REST, SOAP. Тесты на взаимодействия с другими системами, внутреннюю логику. Flow-тесты - тесты на бизнес-флоу для объектов системы (например, заказа). UI-backend тесты. 1) Не важна кроссбраузерность для UI 2) Flow-тесты иногда работают как документация

Slide 12

Slide 12 text

Почему Codeception? ¯\_(ツ)_/¯

Slide 13

Slide 13 text

Почему Codeception? 1. Можно писать и запускать одинаково тесты любых видов (unit, functional, acceptance)

Slide 14

Slide 14 text

Почему Codeception? 1. Можно писать и запускать одинаково тесты любых видов (unit, functional, acceptance) 2. Многие грабли уже решены, много модулей уже написано

Slide 15

Slide 15 text

Почему Codeception? 1. Можно писать и запускать одинаково тесты любых видов (unit, functional, acceptance) 2. Многие грабли уже решены, много модулей уже написано 3. Во всех проектах, несмотря на немного разные потребности, тесты будут выглядеть одинаково

Slide 16

Slide 16 text

Codeception. Основы

Slide 17

Slide 17 text

Стандартные модули 1. PhpBrowser

Slide 18

Slide 18 text

Стандартные модули 1. PhpBrowser 2. REST

Slide 19

Slide 19 text

Стандартные модули 1. PhpBrowser 2. REST 3. Db

Slide 20

Slide 20 text

Стандартные модули 1. PhpBrowser 2. REST 3. Db 4. Cli

Slide 21

Slide 21 text

Стандартные модули 1. PhpBrowser 2. REST 3. Db 4. Cli 5. AMQP

Slide 22

Slide 22 text

Над чем пришлось поработать

Slide 23

Slide 23 text

1. Работа с SOAP SOAP - модуль для тестирования SOAP WSDL сервисов. SOAP: depends: PhpBrowser endpoint: http://serviceapp/api/v1/ $I->sendSoapRequest('UpdateUser', '1notdavert'); $I->seeSoapResponseContainsXPath('//root/user[@id=1]');

Slide 24

Slide 24 text

А если несколько endpoint-ов для SOAP?

Slide 25

Slide 25 text

А если несколько endpoint-ов для SOAP? class SoapWrapper extends \Codeception\Module { public function configure( string $endpoint, string $schema ): void { $this->getModule('SOAP')-> _reconfigure( [ 'endpoint' => $endpoint, 'schema' => $schema, ] ); } }

Slide 26

Slide 26 text

А если несколько endpoint-ов для SOAP? class SoapWrapper extends \Codeception\Module { public function configure( string $endpoint, string $schema $I->getModule('Helper\SoapWrapper') ->configure('endpoint2', 'schema2');

Slide 27

Slide 27 text

2. Что делать с Kafka?

Slide 28

Slide 28 text

2. Что делать с Kafka? Модуля для Kafka нет. Нужно написать свой. - Tests\Support\Module\KafkaModule: config: metadata.broker.list: '%REFUNDS__KAFKA_BROKER_LIST%' group.id: '%REFUNDS__KAFKA_CONSUMER_GROUP_ID%' topic_config: offset.store.sync.interval.ms: '0' auto.commit.interval.ms: '500' auto.offset.reset: 'smallest' Переменные окружения

Slide 29

Slide 29 text

Инициализируем модуль из yml-конфига public function _initialize() { parent::_initialize(); $this->conf = new Conf(); foreach ($this->config['config'] as $key => $value) { $this->conf->set($key, $value); } ... $this->consumer = new Consumer($this->conf); ... }

Slide 30

Slide 30 text

Инициализируем модуль из yml-конфига public function _initialize() { parent::_initialize(); $this->conf = new Conf(); foreach ($this->config['config'] as $key => $value) { $this->conf->set($key, $value); } ... $this->consumer = new Consumer($this->conf); ... }

Slide 31

Slide 31 text

3. Работа с shell Cli - обертка для shell-команд и их output-a. $I->runShellCommand('phpunit'); Но иногда shell-команду нужно запустить не в тестах, а в приложении.

Slide 32

Slide 32 text

Зачем запускать shell в приложении?

Slide 33

Slide 33 text

Как запустить shell в приложении? 1) запускать тесты в контейнере, где находится приложение 2) запускать тесты в отдельном контейнере, но сделать его таким же как приложение

Slide 34

Slide 34 text

4. Работа с файловыми системами

Slide 35

Slide 35 text

С чем нужно работать ● Webdav ● FTP/SFTP ● AWS S3 ● Local ● и др (Azure, Dropbox, google drive)

Slide 36

Slide 36 text

Что можно использовать ● Webdav - ? ● FTP/SFTP - модуль FTP ● AWS S3 - addon https://github.com/polevaultweb/s3-filesystem ● Local - модуль FileSystem ● и др (Azure, Dropbox, google drive)

Slide 37

Slide 37 text

Что можно использовать ● Webdav - ? ● FTP/SFTP - модуль FTP ● AWS S3 - addon https://github.com/polevaultweb/s3-filesystem ● Local - модуль FileSystem ● и др (Azure, Dropbox, google drive) Очень хочется работать со всеми файловыми системами одинаково!

Slide 38

Slide 38 text

Codeception flysystem https://github.com/lamoda/codeception-flysystem Сейчас поддерживаются sftp и webdav. $fileSystem = $I->getFileSystem('sftp'); $fileSystem->clearDir('/path/to/dir'); $fileSystem->writeFile('test.txt', 'Hello world!'); $fileSystem->copyFile('test.txt', 'test_copy.txt'); $fileSystem->deleteFile('test.txt'); $files = $fileSystem->grabFileList('/path/to/dir');

Slide 39

Slide 39 text

5. Базы данных в тестах

Slide 40

Slide 40 text

Основные задачи 1. Как раскатать БД нужной структуры 2. Как заполнить БД тестовыми данными 3. Как делать выборки и проверки

Slide 41

Slide 41 text

Основные задачи 1. Как раскатать БД нужной структуры - Db 2. Как заполнить БД тестовыми данными - Db, Fixtures 3. Как делать выборки и проверки - DB

Slide 42

Slide 42 text

Основные задачи 1. Как раскатать БД нужной структуры - Db 2. Как заполнить БД тестовыми данными - Db, Fixtures 3. Как делать выборки и проверки - DB

Slide 43

Slide 43 text

Разворачивание БД 1. Поднимаем контейнер с PostgreSQL или MySQL. 2. Накатываем все миграции с помощью doctrine migrations.

Slide 44

Slide 44 text

Разворачивание БД 1. Поднимаем контейнер с PostgreSQL или MySQL. 2. Накатываем все миграции с помощью doctrine migrations. Плюсы: 1. Не нужно поддерживать дамп базы для тестов

Slide 45

Slide 45 text

Создание тестовых данных Используем DoctrineFixturesBundle https://github.com/doctrine/DoctrineFixturesBundle $refund = new Refund(); $refund->setRefundMethod('cash'); $refund->setSeller(1); $refund->setCountry('RU');

Slide 46

Slide 46 text

Почему DoctrineFixturesBundle? 1. Проще создавать цепочки связанных объектов 2. Меньше дупликации данных, если фикстуры для разных тестов похожи 3. Меньше правок при изменении структуры БД 4. Фикстуры-классы гораздо нагляднее чем массивы

Slide 47

Slide 47 text

6. Моки для внешних систем

Slide 48

Slide 48 text

Правила для моков 1. Мокаем все внешние http-взаимодействия сервиса 2. Проверяем не только позитивные, но и негативные сценарии

Slide 49

Slide 49 text

Wiremock https://github.com/mcustiel/codeception-wiremock-extension 1. Моки поднимаются в отдельном контейнере https://hub.docker.com/r/rodolpheche/wiremock/ 2. Мок можно создать статически до тестов 3. Мок можно создать динамически в тестах

Slide 50

Slide 50 text

Как работает Wiremock Docker Docker APP Wiremock Request Mapping Response Data { "request": { "method": "POST", "urlPattern": ".*api.*" } { "code": "ok", "error": null }

Slide 51

Slide 51 text

Как динамически создать мок $I->expectRequestToWireMock( WireMock:: get(WireMock::urlEqualTo('/some/url')) ->willReturn(WireMock::aResponse() ->withHeader('Content-Type', 'text/plain') ->withBody('Hello world!')) ); $I->receivedRequestInWireMock( WireMock::getRequestedFor(WireMock::urlEqualTo('/some/url')) );

Slide 52

Slide 52 text

6. Как запускаются тесты

Slide 53

Slide 53 text

Что используется для запуска тестов 1. Docker - все проекты в контейнерах 2. Make - для запуска внутренних команд 3. Bamboo - для CI и деплоев

Slide 54

Slide 54 text

Запуск тестов на CI (bamboo) 1. make build - билдим нужную версию приложения 2. make up - поднимаем окружение a. приложение b. kafka, rabbit и прочее (тоже в docker-e) c. база данных d. накатываем миграции 3. make test - запускаем тесты

Slide 55

Slide 55 text

Запуск тестов на CI (bamboo) 1. make build - билдим нужную версию приложения 2. make up - поднимаем окружение a. приложение b. kafka, rabbit и прочее (тоже в docker-e) c. база данных d. накатываем миграции 3. make test - запускаем тесты 5-10 минут 6-30 минут

Slide 56

Slide 56 text

А как часто надо запускать тесты? Чем чаще - тем лучше!

Slide 57

Slide 57 text

А как часто надо запускать тесты? 1. На каждый чих push 2. На перевод задачи в тестирование 3. На релиз Чем чаще - тем лучше!

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Вопросы Сташевский Павел [email protected] tech.lamoda.ru