Я вижу два варианта организации модульных тестов PHPUnit в иерархии пространства имен. Каковы преимущества / недостатки этих двух подходов? Есть ли очевидные недостатки, которые я не рассматривал, что сделало бы один очевидный лучший выбор?
Рассмотрим образец класса, например \SomeFramework\Utilities\AwesomeClass
:
Подход 1: Поместите каждый класс TestCase в то же пространство имен, что и закрытый класс.
\SomeFramework\Utilities\AwesomeClassTest
Подход 2: Поместите каждую TestCase в пространство имен, названное в честь класса.
\SomeFramework\Utilities\AwesomeClass\Test
Мое предложенное решение и рассуждения позади него:
. ├── src │ ├── bar │ │ └── BarAwesomeClass.php │ └── foo │ └── FooAwesomeClass.php └── tests ├── helpers │ └── ProjectBaseTestClassWithHelperMethods.php ├── integration │ ├── BarModuleTest.php │ └── FooModuleTest.php └── unit ├── bar │ └── BarAwesomeClassTest.php └── foo └── FooAwesomeClassTest.php
helpers/
папка содержит классы, которые не являются тестами, но используются только в контексте тестирования. Обычно эта папка содержит BaseTestClass, возможно, содержит специальные вспомогательные методы проекта и пару простых для повторного использования классов-заглушек, поэтому вам не нужно столько издевок.
Папка integration/
содержит тесты, которые охватывают более классы и проверяют «большие» части системы. У вас их не так много, но нет сопоставления 1: 1 для производственных классов.
Папка unit/
folder отображает 1: 1 в src/
. Поэтому для каждого производственного класса существует один класс, который содержит все модульные тесты для этого класса.
Подход 1: Поместите каждый класс TestCase в то же пространство имен, что и закрытый класс.
Этот подход к папке должен решить один из ваших недостатков с помощью подхода 1 . Вы по-прежнему получаете гибкость, чтобы иметь больше тестов, чем может показаться однозначное сопоставление 1: 1, но все упорядочено и на месте.
Кажется, нарушает принцип использования пространств имен – несвязанные тесты сгруппированы в одно и то же пространство имен.
Если тесты считают «несвязанными», возможно, производственный код имеет такую же проблему?
Это правда, что тесты не зависят друг от друга, но они могут использовать свои «близкие» классы как mocks или использовать реальные в случае DTO или Value Objects. Поэтому я бы сказал, что есть соединение.
Подход 2: Поместите каждую TestCase в пространство имен, названное в честь класса.
Есть несколько проектов, которые это делают, но обычно они структурируют его несколько иначе:
Это не \SomeFramework\Utilities\AwesomeClass\Test
, но \SomeFramework\Tests\Utilities\AwesomeClassTest
и они все еще сохраняют отображение 1: 1, но добавили дополнительное пространство имен тестов.
Мой личный подход заключается в том, что мне не нравится иметь отдельные пространства имен тестов, и я попытаюсь найти пару аргументов и против этого выбора:
Когда настоящий класс находится в другом пространстве имен, тесты показывают, как использовать этот класс вне его собственного модуля.
Когда настоящий класс находится в одном пространстве имен, тесты показывают, как использовать этот класс изнутри этого модуля.
Различия довольно незначительны (обычно это несколько «использования» или полностью квалифицированных путей)
Когда мы получим возможность сказать $this->getMock(AwesomeClass::CLASS)
в PHP 5.5 вместо $this->getMock('\SomeFramework\Utilities\AwesomeClass')
каждый макет потребует использования инструкции.
Для меня использование в модуле более ценно для большинства классов
Когда вы скажете new \SomeFramework\Utilities\A
автозаполнение может показать вам AwesomeClass
и AwesomeClassTest
и некоторые люди этого не хотят. Для внешнего использования или при отправке вашего источника, который не является проблемой, поскольку тесты не отправляются, но это может быть что-то, что нужно учитывать.
Существует третий вариант, который я использую, и который подходит для автозагрузки композитора: Вставьте Test
пространство имен после первого шага в иерархии. В вашем случае это пространство имен \SomeFramework\Tests\Utilities\
и ваш класс будет \SomeFramework\Tests\Utilities\AwesomeClassTest
.
Затем вы можете поставить тесты вместе с другими классами в \SomeFramework\Test
или поместить их в отдельный каталог. Ваша информация об автозагрузке для composer.json
может выглядеть так:
{ "autoload": { "psr-0": { "SomeFramework\\": "src/", } }, "autoload-dev": { "psr-0": { "SomeFramework\\Tests\\": "tests/" } } }
Преимуществами третьего подхода являются:
Я предпочитаю первый подход к поддержанию согласованности – с практикой PHPUnit и другими нашими проектами. Кроме того, я создаю только один тестовый пример для каждого тестируемого класса. Помещение каждого в собственное пространство имен кажется излишним. Как сказал KingCrunch, тесты связаны друг с другом, потому что связанные с ними классы связаны.
Каждый так часто в тестовом случае требуются файлы поддержки, такие как светильники, но они легко организованы в подкаталог / пространство имен, названное для класса, и часто используются для нескольких тестовых случаев.
Одним из главных недостатков второго метода является то, что каждое имя тестового случая является Test
который будет иметь несколько последствий: