PHPUnit лучшие практики для организации тестов

В настоящее время я собираюсь начать с нуля с помощью тестов phpunit для проекта. Поэтому я изучал некоторые проекты (например, Zend), чтобы посмотреть, как они делают вещи и как они организуют свои тесты.

Большинство вещей довольно ясны, только у меня есть некоторые проблемы с тем, как правильно организовать тестовые пакеты. Zend имеет AllTests.php, из которого загружаются другие тестовые сеты.
Тщательно глядя на класс, он использует PHPUnit_Framework_TestSuite для создания объекта набора, а затем добавляет к нему другие пакеты, но если я посмотрю в документах PHPUnit для организации тестов в версиях PHPUnit после 3.4, то есть только описание для XML или FileHierarchy. Тот, который использовал классы для организации тестов, был удален.
Я не нашел ничего, что этот метод устарел, и проекты, подобные Zend, все еще используют его.

Но если он устарел, как я смогу организовать тесты в одной структуре с конфигурацией xml? Выполнение всех тестов не проблема, но как бы я организовал тесты (в xml), если бы я только хотел выполнить несколько тестов. Может быть, создание нескольких xmls, где я только укажу несколько тестов / тестовых наборов для запуска?

Поэтому, если бы я хотел только протестировать module1 и module2 приложения, у меня будет дополнительный xml для каждого и определение наборов тестов только для тех модулей (классов, используемых модулем) в нем. А также тот, который определяет набор тестов для всех тестов?

Или было бы лучше использовать аннотацию @group для конкретных тестов, чтобы отметить их для модуля 1 или модуля2?

Заранее благодарим за то, что вы указали мне некоторые лучшие практики.

Я начну с ссылки на руководство, а затем перейду к тому, что я видел и слышал в поле.

Организация наборов тестов phpunit

Организация модулей / тестовых папок в файловой системе

Мой рекомендуемый подход сочетает файловую систему с xml-конфигурацией.

 tests/ \ unit/ | - module1 | - module2 - integration/ - functional/ 

с phpunit.xml с простым:

 <testsuites> <testsuite name="My whole project"> <directory>tests</directory> </testsuite> </testsuites> во <testsuites> <testsuite name="My whole project"> <directory>tests</directory> </testsuite> </testsuites> 

вы можете разделить testuites, если хотите, но это проект для выбора проекта.

Запуск phpunit затем выполнит ВСЕ тесты, а запущенные phpunit tests/unit/module1 будут запускать все тесты модуля1.

Организация папки «unit»

Наиболее распространенный подход здесь заключается в том, чтобы отразить структуру source/ каталога в структуре ваших tests/unit/ папок.

У вас есть один TestClass на ProductionClass, так что это хороший подход в моей книге.

В организации файлов

  • Один класс для каждого файла.

Это не будет работать в любом случае, если у вас более одного тестового класса в одном файле, чтобы избежать этой ошибки.

  • У вас нет тестового пространства имен

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

Выполнение только нескольких тестов

Например, phpunit --filter Factory выполняет все FactoryTests, в то время как phpunit tests/unit/logger/ выполняет все связанные с phpunit --filter Factory .

Вы можете использовать теги @group для чего-то вроде номеров выпуска, рассказов или чего-то, но для «модулей» я бы использовал макет папки.

Несколько XML-файлов

Может быть полезно создать несколько xml-файлов, если вы хотите:

  • один без покрытия кода
  • один только для модульных тестов (но не для функциональных или интеграционных или долгосрочных тестов)
  • другие распространенные «фильтрующие» случаи
  • PHPBB3, например, делает это для their phpunit.xmls

Покрытие кода для ваших тестов

Поскольку это связано с запуском нового проекта с тестами:

  • Мое предложение состоит в том, чтобы использовать теги @covers как описано в моем блоге (Только для модульных тестов, всегда охватывайте все непубличные функции, всегда используйте теги покрытий.
  • Не создавайте покрытие для ваших интеграционных тестов. Это дает вам ложное чувство безопасности.
  • Всегда используйте белый список, чтобы включить весь ваш производственный код, чтобы цифры не лгали вам!

Автозагрузка и загрузка ваших тестов

Вам не нужна какая-либо автоматическая загрузка для ваших тестов. PHPUnit позаботится об этом.

Используйте <phpunit bootstrap="file"> чтобы указать свой тестовый бутстрап. tests/bootstrap.php – это отличное место для его размещения. Там вы можете настроить автозагрузчик приложений и т. Д. (Или вызывать загрузку приложений в этом случае).

Резюме

  • Используйте xml-конфигурацию для почти всего
  • Индивидуальные тесты и интеграционные тесты
  • Пакеты тестовых папок должны отражать структуру папок ваших приложений
  • Чтобы выполнить только определенные тесты, используйте phpunit --filter или phpunit tests/unit/module1
  • Используйте strict режим от выхода и никогда не выключайте его.

Примеры проектов для просмотра

  • Пример проекта «Банковский счет» Себастьяна Бергманна
  • phpBB3 Даже поэтому им приходится сражаться с их наследием;)
  • Symfony2
  • Doctrine2

Основная структура каталогов :

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

Одно огромное преимущество этого – это значит, что другие сотрудники (а не вы, потому что вы никогда этого не сделаете) будут с меньшей вероятностью избегать написания тестового кода, потому что это слишком много работы. Даже когда они добавляют методы к существующим классам, они с меньшей вероятностью не захотят добавлять тесты к существующему тестовому коду.

Одним из недостатков является то, что это затрудняет выпуск вашего производственного кода без сопровождающих его тестов. Хотя, если вы используете жесткие соглашения об именах, это все равно возможно. Например, я использовал ClassName.php, ClassNameUnitTest.php и ClassNameIntegrationTest.php. Когда я хочу запустить все модульные тесты, есть набор, который ищет файлы, заканчивающиеся на UnitTest.php. Сетка тестирования интеграции работает аналогичным образом. Если бы я хотел, я мог бы использовать подобный метод, чтобы предотвратить выпуск тестов на производство.

Другим недостатком этого подхода является то, что когда вы просто ищете фактический код, а не тестовый код, требуется немного больше усилий, чтобы различать эти два.

Один класс тестов для каждого класса:

Это далеко не экспериментально для большинства программистов, но это для меня. Я экспериментирую, имея только один тестовый класс для каждого тестируемого класса. Раньше у меня был целый каталог для каждого тестируемого класса, а затем у меня было несколько классов внутри этого каталога. Каждый тестовый класс настраивает тестируемый класс определенным образом, а затем имеет кучу методов, каждый из которых имеет другое утверждение. Но затем я начал замечать определенные условия, чтобы эти объекты были связаны с другими условиями, которые он попал в другие классы тестов. Дублирование становится слишком сильным, поэтому я начал создавать абстракции, чтобы удалять его. Тестовый код стал очень трудно понять и поддерживать. Я понял это, но я не видел альтернативы, которая имела смысл для меня. Просто наличие одного тестового класса для каждого класса показалось, что он не сможет протестировать почти достаточно ситуаций, не становясь подавляющим, чтобы иметь весь этот тестовый код внутри одного тестового класса. Теперь у меня другая перспектива. Даже если бы я был прав, это огромный демпфер для других программистов и я, желая писать и поддерживать тесты. Теперь я экспериментирую с тем, чтобы заставить себя тестировать один класс тестов на класс. Если я испытываю слишком много вещей для тестирования в одном тестовом классе, я экспериментирую с тем, что это указывает на то, что тестируемый класс делает слишком много и должен быть разбит на несколько классов. Для устранения дублирования я стараюсь как можно больше придерживаться более простых абстракций, что позволяет всем существовать в одном читаемом классе тестов.