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

У библиотеки PEAR есть замечательный инструмент - PHPUnit, который занимается unit(модульным)-тестированием. Как раз о нем сегодня и пойдет речь.

Установить PHPUnit можно через консоль (глобально):

$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar
$ sudo mv phpunit.phar /usr/local/bin/phpunit

Или же через composer (только для проекта). Для этого в файле composer.json добавляем в зависимости библиотеку:

{
    "name": "project name",
    "require": {
        "phpunit/phpunit": "5.5.*"
    }
}

Сохраняем файл и выполняем команду composer install.

Стоит заметить, что если мы установим PHPUnit глобально, то запускать тесты будем так: phpunit, а если через composer, то так: ./vendor/bin/phpunit.

После установки создаем файл phpunit.xml:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         colors="true">
    <testsuites>
        <testsuite name="UnitTest">
            <directory suffix="Test.php">tests/unit/</directory>
        </testsuite>
    </testsuites>
</phpunit>

В конфигурации мы указываем путь к нашим файлам с тестами (tests/unit/), а также, по желанию, префикс для этих файлов (Test.php). Опции, такие как colors="true" обсуждать не будем, поскольку по названию и так можно догадаться, что они настраивают. Но, при желании, всегда можно почитать о них на официальном сайте.

Ну что ж, теперь давайте приступим к тестированию. А начнем, например, с создания тестов для модели продукта (tests/unit/ProductTest.php):

class ProductTest extends PHPUnit_Framework_TestCase
{

    protected $product;

    protected function setUp()
    {
        $this->product = new Product();
    }

    protected function tearDown()
    {
        $this->product = null;
    }

    /**
     * Проверка на обязательные поля.
     */
    public function testRequiredFields()
    {
        $this->product->name = '';
        $this->product->price = '';

        $this->assertFalse($this->product->save());

        $this->product->name = 'имя продукта';
        $this->product->price = 5000;

        $this->assertTrue($this->product->save());
    }

    /**
     * Проверка на получение категории.
     */
    public function testGetCategory()
    {
        $category = $this->product->getCategory();

        $this->assertInstanceOf(get_class($category), new Category);
        
        $this->assertEquals($category->id, 1);
    }

}

Метод setUp() вызывается перед каждым тестом, tearDown() после каждого теста, а вот testRequiredFields() и testGetCategory() это как раз наши функции тестирования методов объекта Product. Т.е. из примера выше последовательность вызовов будет такая: setUp(), testRequiredFields(), tearDown(), setUp(), testGetCategory(), tearDown().

Такие методы как $this->assertTrue() или $this->assertEquals() - это собственно и есть функции сравнения объектов, которые предоставляет PHPUnit. Со всеми методами сравнения можно ознакомится на официальном сайте PHPUnit.

Ну а теперь давайте запустим наши тесты:

$ phpunit # или `./vendor/bin/phpunit` в зависимости от того как установлен пакет

И посмотрим на результат:

PHPUnit 5.5.0 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 72 ms, Memory: 7.75MB

OK (2 tests, 4 assertions)

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