Пишем тесты с помощью PHPUnit

В примере 2.1 показано, как мы можем писать тесты с использованием PHPUnit, которые выполняют операции массива PHP. В этом примере представлены основные соглашения и шаги для написания тестов с помощью PHPUnit:

  1. Тесты для класса Class переходят в класс с названием ClassTest.
  2. ClassTest наследует (большую часть времени) PHPUnit\Framework\TestCase.
  3. Тесты — это общедоступные методы, которые называются test*.
  4. Кроме того, вы можете использовать аннотацию @test в docblock метода, чтобы пометить ее как метод тестирования.
  5. Внутри методов тестирования, методы утверждения, такие как assertSame(), используются, чтобы утверждать, что фактическое значение соответствует ожидаемому значению.

Пример 2.1 Тестирование операций массива с помощью PHPUnit

 

Martin Fowler:

Всякий раз, когда возникает соблазн ввести что-то в оператор print или выражение отладчика, вместо этого напишите его как тест.

 

Зависимости тестов

Adrian Kuhn et. al.:

Юнит тесты в первую очередь написаны как best practice, помогающая разработчикам выявлять и исправлять ошибки, рефакторинга и служить документацией для тестируемого модуля. Для достижения этих преимуществ юнит-тесты в идеале должны охватывать все возможные пути в программе. Один тест обычно охватывает один конкретный путь в одной функции или методе. Однако метод тестирования не обязательно является инкапсулированным независимым объектом. Часто существуют неявные зависимости между методами тестирования, скрытые в сценарии реализации теста.

 

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

  • Producer (производитель) — это метод тестирования, который возвращает результат тестирования качестве возвращаемого значения.
  • Consumer (потребитель) — это метод тестирования, который зависит от одного или нескольких Producer и их возвращаемых значений.

В примере 2.2 показано, как использовать аннотацию @depends для выражения зависимостей между методами тестирования.

Пример 2.2. Использование аннотации @depends для выражения зависимостей

В приведенном выше примере первый тест testEmpty() создает новый массив и утверждает, что он пуст. Затем тест возвращает фикстуру в качестве результата. Второй тест testPush() зависит от testEmpty() и передается результат этого зависимого теста в качестве аргумента. Наконец, testPop() зависит от testPush().

 

Note:

Возвращаемое значение, полученное producer, по умолчанию передается потребителям «как есть». Это означает, что когда производитель возвращает объект, ссылка на этот объект передается потребителям. Вместо ссылки: (a) (глубокая) копия через @depends clone или (b) возможен также (нормальный) клон (на основе ключевого слова PHP clone) через @depends shallowClone.

 

Чтобы быстро локализовать дефекты, мы хотим, чтобы наше внимание было сосредоточено на соответствующих тестах. Вот почему PHPUnit пропускает выполнение зависящего теста, когда зависимый тест не сработал. Это улучшает локализацию дефектов, используя зависимости между тестами, как показано в примере 2.3.

Пример 2.3 Использование зависимостей между тестами

Тест может содержать более одной аннотации @depends. PHPUnit не изменяет порядок выполнения тестов, вы должны убедиться, что зависимости теста действительно могут быть выполнены до запуска теста.

Тест, который содержит более одной аннотации @depends, получит первый экземпляр первого producer в качестве первого аргумента, второй от второго producer и т.д. См. Пример 2.4.

Пример 2.4. Тестирование с несколькими зависимостями

 

Поставщики данных

Метод тестирования может принимать произвольные аргументы. Эти аргументы должны быть предоставлены одним или несколькими методами передачи данных (additionProvider() в примере 2.5). Используемый метод поставщика данных задается с помощью аннотации @dataProvider.

 

Метод поставщика данных должен быть public и либо возвращать массив массивов, либо объект, реализующий интерфейс Iterator, и выводит массив для каждого шага итерации. Для каждого массива, который является частью коллекции, в качестве аргументов будет вызываться тестовый метод с содержимым массива.

 

Пример 2.5. Использование поставщика данных, который возвращает массив массивов

При использовании большого количества наборов данных полезно называть каждый из них строковым ключом, а не по умолчанию. Результат будет более подробным, так как он будет содержать имя набора данных, который нарушает тест.

Пример 2.6. Использование поставщика данных с именованными наборами данных

Пример 2.7. Использование поставщика данных, который возвращает объект Iterator

Пример 2.8 Класс CsvFileIterator

Когда тест получает входные данные как из метода @dataProvider, так и из одного или нескольких тестов на которые он @depends, аргументы от поставщика данных будут поступать  для зависимых тестов. Аргументы из зависимых тестов будут одинаковыми для каждого набора данных. См. Пример 2.9

Пример 2.9 Комбинация @depends и @dataProvider в одном тесте

 

Пример 2.10. Использование нескольких поставщиков данных для одного теста

Note:

Когда тест зависит от теста, который использует поставщиков данных, зависящий тест будет выполняться, когда тест, от которого он зависит, успешно отработает как минимум для одного набора данных. Результат теста, который использует поставщиков данных, не может быть введен в зависимый тест.

 

 

Тестирование исключений

В примере 2.11 показано, как использовать метод expectException() для проверки того, выбрано ли исключение для проверяемого кода.

Пример 2.11 Использование метода expectException()

В дополнение к методу expectException() существуют методы expectExceptionCode(), expectExceptionMessage() и expectExceptionMessageRegExp(), чтобы установить ожидания для исключений, вызванных тестируемым кодом.

Обратите внимание, что expectExceptionMessage утверждает, что $actual содержит $expected сообщение и не выполняет точное сравнение строк.

Кроме того, вы можете использовать аннотации @expectedException, @expectedExceptionCode, @expectedExceptionMessage и @expectedExceptionMessageRegExp, чтобы настроить ожидания для исключений, вызванных тестируемым кодом. Пример 2.12 показывает пример.

 

Пример 2.12 Использование аннотации @expectedException

 

 

Тестирование ошибок PHP

По умолчанию PHPUnit преобразует ошибки PHP, предупреждения и уведомления, которые запускаются во время выполнения теста в исключения. Используя эти исключения, вы можете, например, ожидать, что тест вызовет ошибку PHP, как показано в примере 2.13.

 

Note:

Конфигурация времени выполнения error_reporting PHP может ограничивать, какие ошибки PHPUnit будет конвертировать в исключения. Если у вас возникли проблемы с этой функцией, убедитесь, что PHP не настроен на подавление типа ошибок, которые вы тестируете.

 

Пример 2.13 Ожидание ошибки PHP с использованием @expectedException

 

 

PHPUnit\Framework\Error\Notice и PHPUnit\Framework\Error\Warning представляют собой уведомления и предупреждения PHP соответственно.

 

Note:

При тестировании исключений вы должны быть как можно более конкретными. Тестирование для классов, которые являются слишком общими, может привести к нежелательным побочным эффектам. Соответственно, тестирование класса Exception с помощью @expectedException или expectException() больше не разрешено.

 

При тестировании, основанном на PHP функциях, которые запускают такие ошибки, как fopen, иногда бывает полезно использовать подавление ошибок во время тестирования. Это позволяет вам проверять возвращаемые значения, подавляя уведомления, которые приведут к PHPUnit\Framework\Error\Notice.

 

Пример 2.14 Тестирование возвращаемых значений кода, использующего ошибки PHP

Без подавления ошибок тест не даст отчета fopen (/is-not-writeable/file): не удалось открыть поток: нет такого файла или каталога.

 

Тестирование вывода (output)

Иногда вы хотите утверждать, что выполнение метода, например, генерирует ожидаемый результат (например, через echo или print). Класс PHPUnit \ Framework \ TestCase использует функцию буферизации вывода PHP Output Buffering  для обеспечения необходимых функций.

В примере 2.15 показано, как использовать метод expectOutputString() для установки ожидаемого результата. Если этот ожидаемый результат не будет сгенерирован, тест не сработает.

Пример 2.15 Тестирование вывода функции или метода

В таблице 2.1 показаны методы, предоставляемые для тестирования вывода

Таблица 2.1 Методы тестирования вывода

 

Метод Смысл
void expectOutputRegex(string $regularExpression) Настройте ожидание того, что результат соответствует $ regularExpression.
void expectOutputString(string $expectedString) Настройте ожидание того, что результат равен $ expectedString.
bool setOutputCallback(callable $callback) Устанавливает обратный вызов, который используется, например, для нормализации фактического вывода.
string getActualOutput() Получить фактический вывод.

 

Note:

Тест, который возвращает вывод, не будет работать в strict режиме.

 

Вывод ошибок

Всякий раз, когда тест не проходит, PHPUnit и пытается предоставить вам максимально возможный контекст, который может помочь выявить проблему.

 

Пример 2.16 Выход ошибки, сгенерированный при сбое сравнения массива

В этом примере только одно из значений массива отличается, а остальные показывают, где возникает ошибка.

Когда сгенерированный вывод будет длинным для чтения, PHPUnit разделит его и предоставит несколько строк контекста вокруг каждой разницы.

Пример 2.17. Вывод ошибки при сравнении массива длинного массива

 

 

«Краеугольный камень»

Когда сравнение не удается, PHPUnit создает текстовые представления входных значений и сравнивает их. Из-за этой реализации diff может показать больше проблем, чем фактически существует.

Это происходит только при использовании assertEquals() или других «слабых» функций сравнения на массивах или объектах.

Пример 2.18. diff при использовании слабого сравнения

В этом примере сообщается разница в первом индексе между 1 и «1», хотя assertEquals() считает значения совпадающими.

https://phpunit.readthedocs.io/ru/latest/writing-tests-for-phpunit.html

Leave a Reply

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.