15 августа 2023

PHPUnit для тестирования WordPress плагинов. Часть 2: Как писать свои тесты

5 минут
phpunit-dlya-testirovaniya-v-wordpress.-chast-2-1

В прошлой статье я рассказал о том, как установить PHPUnit в WordPress плагин. Теперь можно приступить к написанию тестов. Но перед этим рассмотрим зачем вообще нужно писать unit тесты

Главная цель – обеспечение стабильности кода во время его разрастания и усложнения. Грамотно написанные тесты позволят не переживать об уже написанном коде, ведь он точно будет рабочим как и прежде, а если это не так, то тесты покажут проблемные места. Хотя написание тестов и снижает скорость развития проекта на ранних этапах, но дает значительный этап на более поздних этапах разработки

Также наличие тестов один из показателей качества кода – плохой код почти невозможно покрыть тестами из-за запутанности и сильной связанности компонентом системы. Хотя даже покрытый тестами код может быть плохим

Не все unit тесты одинаково полезны для проекта и важно писать хорошие тесты. Хорошие – это те, которые дают как можно меньше ложных срабатываний и больше правильных. Больше об этом можно прочитать в книге Владимира Хорикова “Принципы юнит-тестирования”, а мы вернемся к PHPUnit

Unit тест должен проверять правильность работы одного юнита (единицы поведения) и делать это изолированно от других тестов. Для этого в PHPUnit есть класс TestCase, который содержит всё необходимое для запуска тестов.

Предположим, что нам нужно протестировать класса Bar. Для этого нужно создать класс BarTest в директории с тестами, который будет расширять класс TestCase, вот так:

class Bar {...}<br>class BarTest extends \PHPUnit\Framework\TestCase{...}
class Bar {...}
class BarTest extends \PHPUnit\Framework\TestCase{...}

Теперь пора написать какой-нибудь тест. Раз он первый, то пусть будет простым. Тестировать мы будем метод foo класса Bar

class BarTest extends \PHPUnit\Framework\TestCase{
  public function testFoo() {
    $bar = new Bar(); //1) Создание экземпляра класса
    $x2 = $bar->foo(16); // 2) Вызов метода класса
    $this->assertSame(32, $x2); 3)Проверка результата
  }	
}

Для проверки используется метод assertSame из класса TestCase. Первым параметром ему передается ожидаемое значение, а вторым результат работы. Запуск теста выведет следующую информацию:

OK (1 test, 1 assertion)

Ведь и правда 16 * 2 – это 32. Для проверки результата есть множество других методов: assertTrue и assertFalse для проверки на bool значения или assertEmpty для проверки массива на пустоту.

Больше об основах PHPUnit можно узнать в официальной документации, здесь ее подробно я пересказывать не буду, хотя к некоторым аспектам еще вернусь.

Теперь пора вспомнить, что мы пишем тесты под WordPress и у нас уже должна быть установлена WordPress Tests Library (далее WPTL). Теперь вместо \PHPUnit\Framework\TestCase следует использовать WP_UnitTestCase вот так:

class BarTest extends WP_UnitTestCase{...}

Запустив измененный тест мы не увидим изменений, но они конечно же есть. Класс WP_UnitTestCase наследуется от WP_UnitTestCase_Base, который содержит в себе 1630 строк на момент написания статьи. Самое заметное изменение в работе с тестами – это изменение нотации для написания кода. Теперь вместо camelCase мы все можем писать в snake_case, а по стандартам кода WordPress вообще обязаны.

Важные методы занимающие эти строки – это методы жизненного цикла, стандартный механизм PHPUnit, перед и после запуска каждого метода можно производить любые действия с помощью методов set_up и tear_down соответственно, а также перед и после запуска всех тестов в классе с помощью set_up_before_class и tear_down_after_class. С WPTL теперь нас будет меньше волновать:

  • Подключение к БД
  • Данные в БД
  • Сущности созданные в процессе тестирования
  • Фильтры и экшены
  • Кеш

Также WPTL предоставляет класс WP_UnitTest_Factory, экземпляр которого можно получить через метод ::factory. Знакомые с паттерном фабрика уже поняли, что он делает, для остальных поясняю, этот класс позволяет создавать различные WP сущности: от постов и комментариев до отдельных блогов и их сетей. И это очень удобно, хотя и не понадобится для тестирования каждого плагина

Большинство оставшихся функций используется в жизненном цикле, хотя есть и полезные для отдельных тестов. Например, assertWPError для проверки переменной на WP_Error или go_to, которая принимает url и выставляет все глобальные переменные так, как будто мы сейчас находимся на этой странице. Рекомендую изучить класс WP_UnitTestCase_Base самостоятельно, он хорошо задокументирован и его чтение не будет сложным.

Кроме WP_UnitTestCase есть и другие классы наследующиеся от WP_UnitTestCase_Base. Я использовал только один и сомневаюсь, что остальные пригодятся когда-нибудь. Это класс WP_Ajax_UnitTestCase – класс для тестирования AJAX запросов. Эмулирует вызов определенного action через вызов метода _handleAjax и сохраняет вывод в свойство $_last_response, на основе которого можно уже запускать тестовые методы

Правда появляется вопрос “насколько это unit тест?”. С одной стороны мы проверяем больше чем один unit – регистрацию и запуск обработчика, с другой регистрация и обработка сильно связаны и можно считать их единым целым.

Но самое интересное, что в документации WordPress все тесты с WPTL в плагинах и темах считаются интеграционными, а не unit, ведь для их запуска нужен WordPress, а значит они не самостоятельные. А вот тесты с WPTL в ядре считаются unit тестами, ведь тестируется независимый WordPress

Поэтому если далее я буду говорить не только про unit тесты, но и про интеграционные, не пугайтесь, в коде для нас это одно и тоже.

На этом на сегодня всё. В следующий раз разберем написание тестов на реалистичном примере WordPress плагина

Комментирование этой и других статей доступно в нашем Телеграм канале