Подтвердить что ты не робот

Организация тестов PHPUnit в пространствах имен

Я вижу два варианта организации модульных тестов PHPUnit в иерархии пространства имен. Каковы преимущества/недостатки этих двух подходов? Есть ли очевидные недостатки, которые я не рассматривал, что сделало бы один из очевидных лучших вариантов?

Рассмотрим образец класса типа \SomeFramework\Utilities\AwesomeClass:

  • Подход 1: Поместите каждый класс TestCase в то же пространство имен, что и закрытый класс.

    \SomeFramework\Utilities\AwesomeClassTest
    
    • Преимущества
      • В соответствии с традиционным подходом к написанию тестов PHPUnit.
    • Недостатки
      • Меньше гибкости.
      • Кажется, нарушает принцип использования пространств имен - несвязанные тесты сгруппированы в одно и то же пространство имен.


  • Подход 2: Поместите каждую TestCase в пространство имен, указанное после класса.

    \SomeFramework\Utilities\AwesomeClass\Test
    
    • Преимущества
      • Обеспечивает очень простой/очевидный способ группировать несколько связанных классов TestCase вместе, например, для разных тестовых наборов.
    • Недостатки
      • Может привести к более глубокой и более сложной иерархии.
4b9b3361

Ответ 1

Мое предложенное решение и рассуждения позади него:

Макет папки:

.
├── 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/ отображает 1:1 в src/. Поэтому для каждого производственного класса существует один класс, содержащий все тесты unit для этого класса.

Пространство имен

Подход 1: Поместите каждый класс TestCase в то же пространство имен, что и класс.

Этот подход к папке должен решить один из ваших недостатков: Подход 1. Вы по-прежнему получаете гибкость, чтобы иметь больше тестов, чем может показаться однозначное сопоставление 1:1, но все упорядочено и находится на месте.

Кажется, нарушает принцип использования пространств имен - несвязанные тесты сгруппированы в одно и то же пространство имен.

Если тесты кажутся "несвязанными", возможно, производственный код имеет такую ​​же проблему?

Верно, что тесты не зависят друг от друга, но они могут использовать свои "близкие" классы как mocks или использовать реальные в случае DTO или Value Objects. Поэтому я бы сказал, что есть соединение.

Подход 2: Поместите каждую TestCase в пространство имен, названное после класса.

Есть несколько проектов, которые это делают, но обычно они структурируют его несколько иначе:

Это не \SomeFramework\Utilities\AwesomeClass\Test, но \SomeFramework\Tests\Utilities\AwesomeClassTest, и они все еще сохраняют отображение 1:1, но с добавлением дополнительного пространства имен.

Экспериментальное пространство имен

Мой личный подход заключается в том, что мне не нравится иметь отдельные тестовые пространства имен, и я попытаюсь найти пару аргументов и против этого выбора:

Тесты должны служить документацией о том, как использовать класс

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

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

Различия довольно незначительные (обычно это несколько инструкций "use" или полностью квалифицированных путей)

Когда мы получим возможность сказать $this->getMock(AwesomeClass::CLASS) в PHP 5.5 вместо $this->getMock('\SomeFramework\Utilities\AwesomeClass'), каждый макет потребует использования оператора.

Для меня использование в модуле более ценно для большинства классов

Загрязнение пространства имен "Производство"

Когда вы скажете new \SomeFramework\Utilities\A, автозаполнение может показать вам AwesomeClass и AwesomeClassTest, а некоторые люди этого не хотят. Для внешнего использования или при отправке источника, который не является проблемой, поскольку тесты не отправляются, но это может быть что-то, что нужно учитывать.

Ответ 2

Есть третий вариант, который я использую, и который хорошо подходит для автозагрузки композитора: вставьте пространство имен Test после первого шага в иерархии. В вашем случае это пространство имен будет \SomeFramework\Tests\Utilities\ а ваш класс будет \SomeFramework\Tests\Utilities\AwesomeClassTest.

Затем вы можете поместить тесты вместе с другими классами в \SomeFramework\Test или поместить их в отдельный каталог. Ваша информация для автозагрузки composer.json может выглядеть так:

{
    "autoload": {
        "psr-0": { 
            "SomeFramework\\": "src/",
        }
    },
    "autoload-dev": {
        "psr-0": { 
            "SomeFramework\\Tests\\": "tests/"
        }
    }
}

Преимущества третьего подхода:

  • Разделение тестов и производственного кода
  • Подобные иерархии папок для тестов и производственных классов
  • Простая автозагрузка

Ответ 3

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

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

Одним из главных недостатков второго метода является то, что каждое имя тестового случая Test, которое будет иметь несколько разветвлений:

  • Несколько открытых тестовых окон будут иметь одинаковое имя.
  • Функция IDE открытого типа по имени (CTRL-O в NetBeans) будет бесполезной для тестов.
  • Идентификатор ключевого слова IDE "перейти к тестированию" (CTRL-SHIFT-T в NetBeans) также может завершиться неудачей.