Skip to main content

Блог переїхав

Блог переїхав на HUGO . Нова адреса сайту  https://sheremetat.dev

Установка и настройка PHPUnit

PHPUnit - мощный фреймфорк для тестирования приложений написаных на языке PHP. С его помощью можно избегать ошибок, которые делают разработчики (а мы их таки делаем!), а также, при правильном использовании, создавать более "красивый" и читабельный код.

В этой статье я вкратце рассмотрю основные правила написания тестов, установку PHPUnit, а также "наведем красоту" в выводе результатов выполнения теста. Также сделаем шаблон для каждого нашего будущего теста.


PHPUnit можно (и нужно) использовать как основу для TDD (Test Driving Developing). Если писать тест перед написанием собственно кода приложения, то резко увеличивается качестко кода.
Но не все так просто. Для того, чтобы тесты были хорошими необходимо придержыватся нескольких простых правил:
  • Тесты должны легко читатся - тест не должен содержать какие-либо внешние переопределения, чтобы тест не потерялся в шуме, который его окружает
  • Тесты должны легко и быстро выполнятся
  • Тесты должны быть изолированными - тесты не должны влиять друг на друга. Если порядок выполнения тестов изменяется, итоговый результат меняться не должен
  • Тесты должны быть изолироваными - должна быть возможность запускать любую комбинацию тестов. Это следствие изолированности
PHPUnit больше склоняется в сторону изолированности в ущерб скорости выполнения. Изолированные тесты очень важны, поскольку они обеспечивают высококачественную обратную связь. Использование изолированных тестов поощряет разработчика использовать большое количество простых объектов. Изолированно можно быстро проверить все эти объекты. В результате архитектура становится лучше, а тесты быстрее.

Тест должен быть минимально возможного размера, и должен проверять только одну характеристику объекта. Это своего рода искусство, выполнять тестирование при помощи множества маленьких тестов. Применение микротестов позволяет в целом улучшить архитектуру системы.

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

Установка

Для установки PHPUnit используется PEAR Installer. Сначала необходимо добавить два канала
pear channel-discover pear.phpunit.de
pear channel-discover pear.symfony-project.com
Потом установить сам PHPUnit:
pear install phpunit/PHPUnit
Скорее всего могут возникнуть проблемы с недостающими модулями. Например, вам будет необходимо установить несколько PEAR пакетов и несколько модулей PHP. Одним из них есть xdebug. И не просто модуль, а его последнюю версию. Для этого вам сначала понадобится модуль php5-dev. Для установки используется команда :
apt-get install php5-dev
А потом собрать сам xdebug:
pecl install xdebug
Для установки модулей pdo_mysql и pdo_sqlite сначала надо установить libmysqlclient15-dev, а потом сами модули, используя команды описаны выше.
Также может возникнуть проблема с PEAR Installer. Для обновления до последней версии используйте команду:
pear upgrade PEAR-1.9.0
Все должно получится. Если в консоле ввести команду phpunit, вы увидите версию фреймворка и справку.

Коротко о написании теста

Для того чтобы написать тест, нужно сделать несколько простых шагов:
  • Наименование тестирующего класса образуется путем добавления постфикса Test к наименованию тестируемого класса. Например тестируемый класс называется Class, тестирующий - ClassTest
  • ClassTest наследуется от PHPUnit_Framework_TestCase
  • Наименование тестирующих методов образуются путем приставки test к наименованию тестируемых методов
  • Внутри тестовых методов утверждения задаются специальными функциями assertEquals() . assertEquals() задает соответствие реально полученного значения и ожидаемого.
В начале теста надо подключить фреймворк.
Вот простой пример, взятый с сайта официальной документации:
require_once 'PHPUnit/Framework.php';

class StackTest extends PHPUnit_Framework_TestCase
{
  public function testPushAndPop()
  {
    $stack = array();
    $this->assertEquals(0, count($stack));

    array_push($stack, 'foo');
    $this->assertEquals('foo', $stack[count($stack)-1]);
    $this->assertEquals(1, count($stack));

    $this->assertEquals('foo', array_pop($stack));
    $this->assertEquals(0, count($stack));
  }
}
Для того, чтобы запустить тест в консоле перейдите в папку с тестом и выполнить команду:
phpunit filename.php
В консоле вы увидите приблизительно следующее:
Для того, чтобы сделать вывод немного привлекательней добавим в команду параметр --colors:
phpunit --colors filename.php
И вывод станет немного лучше:


Создание класса-родителя для всех будущих тестов

Назовем его ControllerTestCase. Этот класс будет наследоватся от PHPUnit_Framework_TestCase и переопределять несколько его методов.

PHPUnit поддерживает совместное использование кода установки тестового окружения.
До того как начнет выполняться метод тестирования, будет вызван шаблонный метод setUp().
Как только метод тестирования завершит свою работу будет вызван другой шаблонный метод - tearDown(). Причем его вызов не зависит от того успешно ли завершился тест или нет.

Их мы и переопределим. Все нашы изменения будут в том, чтобы вывести (цветными, разумеется)  тестируемый класс, метод и время выполнения теста. Код этого класса очевиден, так что просто привожу его ниже:
abstract class ControllerTestCase extends PHPUnit_Framework_TestCase {

  public function setUp() {

    parent::setUp();
    $class = $this->_colorize(get_class($this), 'blue');
    $method = $this->_colorize($this->getName());
    echo "\n" . $class . ' -> ' . $method;
    $this->_time = microtime(1);
  }

  public function tearDown() {

    $time = sprintf('%0.3f sec', microtime(1) - $this->_time);
    echo "\t" . $this->getCount() . '(Assertions)';
    echo $this->_colorize("\t" . $time, 'green');
    parent::tearDown();
  }

  private function _colorize($text, $color = 'yellow')
  {
    switch ($color) {
      case 'red':
        $color = "1;31m";
        break;
      case 'green':
        $color = "1;32m";
        break;
      case 'blue':
        $color = "1;34m";
        break;
      default:
        $color = "1;33m";
        break;
    }
    return "\033[" . $color . $text . "\033[m";
  }
}
Теперь наш код простого теста немного изменился. Если из него исключить метод testPushAndPop(), то мы получим шаблон всех наших будущих тестов.
require_once 'PHPUnit/Framework.php';
require_once 'ControllerTestCase.php';

class StackTest extends ControllerTestCase
{
  public function setUp() {
    parent::setUp();
  }


  public function testPushAndPop()
  {
    $stack = array();
    $this->assertEquals(0, count($stack));

    array_push($stack, 'foo');
    $this->assertEquals('foo', $stack[count($stack)-1]);
    $this->assertEquals(1, count($stack));

    $this->assertEquals('foo', array_pop($stack));
    $this->assertEquals(0, count($stack));
  }

  public function tearDown() {
    parent::tearDown();
  }
}
Результат будет выглядеть так в случае успешного прохождения теста
И случае неудачи

Выводы

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

За дополнительной информацией обращайтесь на сайт PHPUnit в раздел документации.
Всем удачи и спасибо за внимание.