Написание тестов
Содержание
Видение
Мы хотим сделать ручное тестирование ненужным для разработчиков ядра, авторов плагинов и администраторов сайтов, продвигая и обеспечивая быстрое автоматизированное тестирование на каждом уровне стека Elgg.
Мы с нетерпением ждём мира, в котором разработчикам ядра не нужно будет проводить ручное тестирование для проверки корректности кода, внесённого в Elgg. Аналогично, мы представляем мир, в котором администраторы сайтов могут обновлять и устанавливать новые плагины с уверенностью, что всё работает хорошо вместе.
Запуск тестов
Набор тестов ядра Elgg
- В настоящее время наши тесты разделены на две части:
Тесты PHPUnit расположены в
/tests/phpunit— они разделены на модульные тесты и интеграционные тесты.
Поскольку у нас есть конфигурация phpunit.xml в корне Elgg, тестирование должно быть таким же простым, как:
git clone http://github.com/Elgg/Elgg
cd Elgg
phpunit
Если вы пишете модульный тест, вы можете расширить класс \Elgg\UnitTestCase, или если вы пишете интеграционный тест, вы можете расширить класс \Elgg\IntegrationTestCase. Эти классы имеют некоторые вспомогательные функции при написании тестов. Часто используются следующие функции:
up()— используется для подготовки каждого теста, выполняемого в тестовом случае
down()— выполняется после каждого теста, выполняемого в тестовом случае. В основном используется для очистки/восстановления.
createUser()— создаёт сущностьElggUserдля использования в вашем тесте
createGroup()— создаёт сущностьElggGroupдля использования в вашем тесте
createObject()— создаёт сущностьElggObjectдля использования в вашем тесте
Тесты плагинов
В идеале плагины настроены таким образом, чтобы их можно было тестировать модульно, как и ядро Elgg. Разработчики плагинов могут свободно реализовывать свои собственные методы модульного тестирования, но мы призываем всех сделать это так же просто, как в ядре Elgg:
git clone http://github.com/developer/elgg-plugin plugin
cd plugin
phpunit
Сквозные тесты
Поскольку плагины Elgg обладают большой мощностью для переопределения, фильтрации и изменения поведения Elgg и других плагинов, важно иметь возможность запускать сквозные тесты на промежуточном сервере с вашей окончательной конфигурацией перед развёртыванием в продакшен.
Примечание
Задача: Сделать лёгким запуск всех интеграционных и приёмочных тестов Elgg из панели администратора при текущей конфигурации плагинов. (не беспокоясь о повреждении базы данных!)
Мотивация
- Кратко, преимущества, которые мы ожидаем от тестирования:
Повышенная уверенность в системе.
Больше свободы для рефакторинга.
Встроенная, актуальная документация.
Мы любим вклад сообщества, но для поддержания стабильности мы не можем принимать внешние вклады без предварительной проверки их корректности. Продвигая автоматизированное тестирование, разработчики ядра могут избежать хлопот ручной проверки перед принятием патчей. Это также означает, что внешним разработчикам не нужно тратить время на завоевание доверия команды ядра. Если приходит патч и имеет тесты для его проверки, тогда мы можем быть уверены, что он работает, не беспокоясь о репутации автора.
Обратите внимание, что эти преимущества также могут распространяться на репозиторий плагинов. Владельцам сайтов рекомендуется «тщательно тестировать плагины» перед их развёртыванием на боевом сайте. По состоянию на март 2013 года это означает ручную проверку всех функций, которые обещает предоставить плагин. Но Elgg предоставляет огромное количество функций, и неразумно тестировать все из них в каждом браузере, который вы хотите поддерживать, на каждом устройстве, которое вы хотите поддерживать. Но что, если разработчики плагинов могли бы писать тесты для своих плагинов, а владельцы сайтов могли бы просто запускать тесты для всех установленных плагинов, чтобы убедиться, что функциональность сохраняется? Тогда они не были бы ограничены выбором плагинов только от «надёжных» разработчиков или «стабильных» релизов. Они могли бы увидеть, что действительно ничего не сломалось, когда они обновили этот критический плагин с 1.3 до 2.5, и с уверенностью продвинуть обновление в продакшен.
Причина, по которой этого не происходит сегодня, заключается в том, что сам Elgg ещё не легко тестируем на этом уровне. Мы хотим это изменить.
Стратегия
У нас есть несколько руководящих принципов, которые, по нашему мнению, помогут воплотить наше видение в реальность.
- Короче говоря, мы пропагандируем:
Непрерывная интеграция — если проверки GitHub не проходят, мы не довольны
Внедрение зависимостей — для создания высоко тестируемого, модульного кода
BDD — тесты должны проверять функции и предоставлять документацию, а не пересказывать API класса
Непрерывная интеграция
Мы запускаем все наши тесты на GitHub Actions, чтобы получать обратную связь в реальном времени о корректности входящих запросов на слияние и разработки по мере её продвижения. Если проверки GitHub не проходят, мы не коммитим в репозиторий. Это даёт нам возможность быстро сливать запросы на слияние, пока они проходят тесты. Это также позволяет нам отклонять запросы на слияние без детального расследования, если они не проходят тесты. Мы можем выйти за рамки вопроса «работает это или нет» и говорить о том, о чём людям нужно говорить: дизайн API, полезность для проекта, принадлежит ли это ядру или плагину и т.д. Мы хотим, чтобы как можно больше функций, предоставляемых ядром Elgg, проверялись автоматически тестами, запущенными на GitHub Actions.
Внедрение зависимостей
Для максимальной тестируемости все зависимости должны быть явными. Глобальные функции, синглтоны и локаторы сервисов губительны для тестируемости, потому что невозможно определить, какие зависимости скрываются под капотом, и ещё сложнее замокать эти зависимости. Мокинг критически важен, потому что вы хотите, чтобы ваши модульные тесты тестировали только один класс за раз. Сбои тестов в TestCase не должны происходить из-за поломки в зависимости; сбои тестов должны указывать только на поломку в тестируемом классе. Это делает всё гораздо проще для отладки. По состоянию на март 2013 года большая часть Elgg всё ещё предполагает и использует глобальное состояние, и это исторически делало Elgg и плагины Elgg очень сложными для тестирования. К счастью, сейчас мы движемся в противоположном направлении, и большая часть работы в Elgg 1.9 была направлена на рефакторинг компонентов ядра для большей возможности внедрения зависимостей. Мы уже пожинаем плоды этих усилий.
Разработка, управляемая поведением (BDD)
Для нас это означает, что мы называем тесты по функциям, а не по методам. Когда вы тестируете функции, вам рекомендуется писать меньше, меньших, логических тестов. Когда тест не проходит, мы можем точно знать, какая функция нарушена. Более того, при именовании ваших тестов по функциям список тестов предоставляет документацию о том, какие функции поддерживает система. Документация — это то, что обычно очень сложно поддерживать в актуальном состоянии, но когда документация и проверка — это одно и то же, становится очень легко поддерживать документацию в актуальном состоянии.
- Рассмотрите эти два тестовых метода:
testRegister()testCanRegisterFilesAsActionHandlers()
Просто глядя на имена, testRegister говорит вам, что тестируемый класс, вероятно, имеет метод с именем register. Если этот тест проходит, он, по-видимому, проверяет, что он ведёт себя правильно, но не говорит вам, что влечёт за собой правильное поведение или что намеревался проверить первоначальный автор теста. Если этот метод имеет несколько правильных применений, для которых вам нужно провести тестирование, это краткое соглашение об именовании также побуждает вас написать очень длинный тест, который проверяет все условия и функции указанного метода. Сбой теста может быть вызван нарушением любого из этих применений, и потребуется больше времени, чтобы выяснить, где кроется истинная проблема.
С другой стороны, testCanRegisterFilesAsActionHandlers говорит вам, что есть такие вещи, называемые «действиями», которые нужно «обрабатывать», и что файлы могут быть зарегистрированы как действительные обработчики для действий. Это знакомит новичков с терминологией проекта и чётко передаёт намерение теста тем, кто уже знаком с терминологией.
Хороший пример того, что мы ищем, см. в /tests/phpunit/Elgg/ViewServiceTest.php