Pull to refresh

PHPUnit. Часть 03 Написание тестов для PHPUnit

PHP *
Translation
Original author: Sebastian Bergmann
Предисловие переводчика
Эта статья продолжает серию переводов официальной документации по PHPUnit на русский язык.
Часть 1, Часть 2

Пример 4.1 демонстрирует как с помощью PHPUnit можно выполнить тестирование операций с массивами PHP. В этом примере показаны базовые соглашения и шаги, свойственные тестам PHPUnit:
  • Наименование тестирующего класса образуется путем добавления постфикса Test к наименованию тестируемого класса. Например тестируемый класс называется Class, тестирующий — ClassTest.
  • ClassTest наследуется (в большинстве случаев) от PHPUnit_Framework_TestCase.
  • Наименования тестирующих методов образуются путем добавления приставки test к наименованиям тестируемых методов.
  • Внутри тестовых методов утверждения задаются специальными функциями assertEquals()
    (смотрите раздел PHPUnit_Framework_Assert). assertEquals() задает соответствие реально полученного значения и ожидаемого.
Пример 4.1: Тестирование операций с массивами PHP при с использованием PHPUnit
<?php<br>
require_once 'PHPUnit/Framework.php';<br>
<br>
class StackTest extends PHPUnit_Framework_TestCase<br>
{<br>
    public function testPushAndPop()<br>
    {<br>
        $stack = array();<br>
        $this->assertEquals(0, count($stack));<br>
<br>        array_push($stack, 'foo');<br>
        $this->assertEquals('foo', $stack[count($stack)-1]);<br>
        $this->assertEquals(1, count($stack));<br>
<br>        $this->assertEquals('foo', array_pop($stack));<br>
        $this->assertEquals(0, count($stack));<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.


«Пишите тест в независимости от того хотите ли Вы вывести значение через функцию print или
выражение отладки.»
Martin Fowler

Зависимости тестов


«Unit-тесты обычно пишутся, чтобы помочь разработчику найти и исправить ошибки, выполнить рефакторинг кода и облегчить документирование модулей.
Чтобы достичь эти цели, тесты в идеале должны покрывать все возможные пути в программе.
Обычно один тест покрывает один специфический путь в одной функции или методе.
Однако тестовый метод не обязательно может быть инкапсулированной, независимой сущностью.
Часто существуют не очевидные зависимости между тестами, скрытые в реализации тестовых сценариев».
Adrian Kuhn et. al.
PHPUnit поддерживает декларирование явных зависимостей между тестами.
Такие зависимости не определяют в какой последовательности тесты должны выполняться,
однако они позволяют возвращать экземпляр тестового окружения (fixture) и передавать его в другой тест; источник (producer) передает приемнику(consumer).
  • Источник — это тестовый метод, который возвращает значения, от которых зависят другие методы модуля.
  • Приемник — это тестовый метод, который зависит от одного или более источников и их возвращаемых значений.
Пример 4.2 показывает как использовать аннотацию @depends для описания зависимостей между тестами.

Пример 4.2: Использование аннотации @depends для описания зависимостей
<?php<br>
class StackTest extends PHPUnit_Framework_TestCase<br>
{<br>
    public function testEmpty()<br>
    {<br>
        $stack = array();<br>
        $this->assertTrue(empty($stack));<br>
<br>
        return $stack;<br>
    }<br>
<br>
    /**<br>
     * @depends testEmpty<br>
     */<br>
    public function testPush(array $stack)<br>
    {<br>
        array_push($stack, 'foo');<br>
        $this->assertEquals('foo', $stack[count($stack)-1]);<br>
        $this->assertFalse(empty($stack));<br>
<br>
        return $stack;<br>
    }<br>
<br>
    /**<br>
     * @depends testPush<br>
     */<br>
    public function testPop(array $stack)<br>
    {<br>
        $this->assertEquals('foo', array_pop($stack));<br>
        $this->assertTrue(empty($stack));<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.

В этом примере первый тест, testEmpty(), создает пустой массив и задает утверждение, что массив пустой. После этого тест возвращает окружение (fixture) в качестве результата.
Второй тест, testPush(), зависит от testEmpty() и получает результат работы testEmpty() в качестве аргумента. И наконец, testPop() зависит от testPush().
Мы хотим, чтобы наше внимание фокусировалось на важных сообщениях о провале тестов, это позволит максимально быстро находить дефекты.
По этой причине PHPUnit пропускает выполнение зависимых тестов, если Источник (основной тест) закончил работу с ошибкой. Улучшение обнаружения дефектов достигается за счет использования зависимостей между тестами, как показано в Примере 4.3.

Пример 4.3: Использование зависимостей между тестами
<?php<br>
class DependencyFailureTest extends PHPUnit_Framework_TestCase<br>
{<br>
    public function testOne()<br>
    {<br>
        $this->assertTrue(FALSE);<br>
    }<br>
<br>
    /**<br>
     * @depends testOne<br>     */<br>
    public function testTwo()<br>    {<br>
    }<br>}<br>?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.
phpunit --verbose DependencyFailureTest
PHPUnit 3.4.2 by Sebastian Bergmann.

DependencyFailureTest
FS

Time: 0 seconds

There was 1 failure:

1) testOne(DependencyFailureTest)
Failed asserting that <boolean:false> is true.
/home/sb/DependencyFailureTest.php:6

There was 1 skipped test:

1) testTwo(DependencyFailureTest)
This test depends on "DependencyFailureTest::testOne" to pass.

FAILURES!
Tests: 2, Assertions: 1, Failures: 1, Skipped: 1.

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

Источники данных (Data Providers)


Тестовый метод может работать с произвольными аргументами.
Аргументы передаются с помощью метода источника данных (provider(), см. Пример 4.4).
Метод источника данных должен быть определен с помощью аннотации @dataProvider.
Метод источника данных должен быть public и должен возвращать массив массивов или объект, поддерживающий интерфейс Iterator, который на каждой итерации возвращает массив.
Для каждого массива, который является частью коллекции, будет вызван тестовый метод. В качестве аргумента методу будет передан массив значений.

Пример 4.4: Использование источника данных
<?php<br>
class DataTest extends PHPUnit_Framework_TestCase<br>
{<br>
    /**<br>
     * @dataProvider provider<br>
     */<br>
    public function testAdd($a, $b, $c)<br>
    {<br>
        $this->assertEquals($c, $a + $b);<br>
    }<br>
<br>
    public function provider()<br>
    {<br>
        return array(<br>
         array(0, 0, 0),<br>
         array(0, 1, 1),<br>
         array(1, 0, 1),<br>
         array(1, 1, 3)<br>
        );<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.

phpunit DataTest
PHPUnit 3.4.2 by Sebastian Bergmann.

...F

Time: 0 seconds

There was 1 failure:

1) testAdd(DataTest) with data (1, 1, 3)
Failed asserting that <integer:2> matches expected value <integer:3>.
/home/sb/DataTest.php:21

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.

Примечание


Если тестовому методу одновременно передаются параметры от источника данных (@dataProvider) и одного или нескольких тестов, которые определены как зависимые (@depends), то в первую очередь используется источник данных и только потом другие тесты.

Тестирование исключений


Пример 4.5 демонстрирует как использовать аннотацию @expectedException для тестирования
исключений, которые выбрасываются внутри тестового кода.

Пример 4.5: Использование аннотации @expectedException
<?php<br>
require_once 'PHPUnit/Framework.php';<br>
<br>
class ExceptionTest extends PHPUnit_Framework_TestCase<br>
{<br>
    /**<br>
     * @expectedException InvalidArgumentException<br>
     */<br>
    public function testException()<br>
    {<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.

phpunit ExceptionTest
PHPUnit 3.4.2 by Sebastian Bergmann.

F

Time: 0 seconds

There was 1 failure:

1) testException(ExceptionTest)
Expected exception InvalidArgumentException

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Метод setExpectedException() — это еще один способ указать, что в тестовом методе выбрасывается исключение, см. Пример 4.6.

Пример 4.6: Ожидается, что тестовый метод выбросит исключение
<?php<br>
require_once 'PHPUnit/Framework.php';<br>
<br>
class ExceptionTest extends PHPUnit_Framework_TestCase<br>
{<br>
    public function testException()<br>
    {<br>
        $this->setExpectedException('InvalidArgumentException');<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.

phpunit ExceptionTest
PHPUnit 3.4.2 by Sebastian Bergmann.

F

Time: 0 seconds

There was 1 failure:

1) testException(ExceptionTest)
Expected exception InvalidArgumentException

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

В таблице 4.1 приведены методы для тестирования исключений.

Таблица 4.1 (текстовый вариант). Методы для тестирования исключений
void setExpectedException(string $exceptionName)
— Установка имени ожидаемого исключения — $exceptionName.
String getExpectedException()
— Получение имени ожидаемого исключения.

Для тестирования исключений Вы можете использовать подход, показанный в Примере 4.7.

Пример 4.7: Альтернативный подход к тестированию исключений
<?php<br>
require_once 'PHPUnit/Framework.php';<br>
<br>
class ExceptionTest extends PHPUnit_Framework_TestCase {<br>
    public function testException() {<br>
        try {<br>
            // ... Code that is expected to raise an exception ...<br>
        }<br>
<br>
        catch (InvalidArgumentException $expected) {<br>
            return;<br>
        }<br>
<br>
        $this->fail('An expected exception has not been raised.');<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.



Ожидается, что код, показанный в Примере 4.7 выбросит исключение. Если это не произойдет, то будет вызван метод fail(), (см.
(см. Таблицу 22.2), который прервет выполнение теста и просигнализирует об ошибке.
Если исключение будет сгенерированно, то сработает блок catch и тест завершится успешно.

Тестирование ошибок PHP


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

Пример 4.8: Применение @expectedException для ожидяния ошибки PHP
<?php<br>
class ExpectedErrorTest extends PHPUnit_Framework_TestCase<br>
{<br>
    /**<br>
     * @expectedException PHPUnit_Framework_Error<br>
     */<br>
    public function testFailingInclude()<br>
    {<br>
        include 'not_existing_file.php';<br>
    }<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter.

phpunit ExpectedErrorTest
PHPUnit 3.4.2 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)

PHPUnit_Framework_Error_Notice и PHPUnit_Framework_Error_Warning
представляют PHP уведомления и предупреждения.


Продолжение:
Часть 4
Tags: phpphpunitтестированиеunit testing
Hubs: PHP
Total votes 31: ↑23 and ↓8 +15
Comments 12
Comments Comments 12

Popular right now