Зарегистрируйтесь, чтобы продолжить обучение

Data Provider PHP: Автоматическое тестирование

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

Рассмотрим простой пример. У нас есть функция cube(int $x): int, которая возвращает куб числа. Вместо того чтобы писать несколько однотипных тестов, мы можем воспользоваться механизмом Data Provider, встроенным в PHPUnit.

Что такое Data Provider?

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

Пример теста с использованием Data Provider:

<?php

namespace Hexlet\Package\Tests;

// Важно импортировать DataProvider, чтобы мы могли его использовать
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

use function Hexlet\Package\Math\cube;

class MathTest extends TestCase
{
    // Связываем метод-провайдер с методом-тестом
    #[DataProvider('cubeProvider')]
    public function testCube(int $expected, int $argument): void
    {
        $this->assertEquals($expected, cube($argument));
    }

    // cubeProvider() - это метод-провайдер
    public static function cubeProvider(): array
    {
        return [
            [1, 1],
            [8, 2],
            [27, 3],
            [64, 4],
        ];
    }
}

В этом примере:

  • Метод testCube() получает два аргумента: ожидаемое значение $expected и входной параметр $argument.
  • Метод cubeProvider() возвращает массив массивов — каждый вложенный массив содержит параметры для одного запуска теста.
  • PHPUnit сам подставит пары значений в тестовый метод.

Как работают методы провайдеры

Как метод testCube() узнает про метод-провайдер? За счет вызова атрибута #[DataProvider('cubeProvider')]. Атрибуты в PHP — это специальные метки, которые пишутся в квадратных скобках #[...] над функциями, классами, переменными. Они не выполняются сами по себе, а просто сообщают PHP (или другим инструментам), что с этим кодом делать.

<?php

// ...
#[DataProvider('cubeProvider')]
public function testCube(int $expected, int $argument): void
// ...

Здесь #[DataProvider('cubeProvider')] — это атрибут. Он говорит PHPUnit: «Эта функция будет запускаться несколько раз — с разными значениями, которые вернёт cubeProvider».

Зачем нужны провайдеры данных?

  1. Снижение дублирования кода. Один тест — множество проверок.
  2. Читаемость вывода. При ошибке PHPUnit покажет, с какими данными тест упал.
  3. Независимость запусков. Каждый набор данных — отдельный запуск, поэтому падение одного не влияет на остальные.
  4. Удобство отладки. Легче понять, какой именно вход привёл к ошибке.

Именованные наборы данных

PHPUnit позволяет дать имена наборам данных. Посмотрите на пример ниже:

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Hexlet\Package\User;

class UserTest extends TestCase
{
    #[DataProvider('userDataProvider')]
    public function testGetName(string $name, array $children): void
    {
        $user = new User($name, $children);

        $this->assertEquals($name, $user->getName());
        $this->assertEquals(collect($children), $user->getChildren());
    }

    public static function userDataProvider(): array
    {
        return [
            'one·child' => ['john', [new User('Mark')]],
            'no children' => ['anna', []],
            'two children' => ['alice', [new User('Tom'), new User('Eva')]],
        ];
    }
}

Если запустить этот тест с опцией --testdox, то мы получим вывод имени тестового метода и набора данных, с которым были запущены тесты

composer exec --verbose phpunit tests/UserTest.php -- --testdox
> __exec_command: phpunit 'tests/UserTest.php' '--testdox'
PHPUnit 12.1.4 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.0
Configuration: /hexlet-php-testing/phpunit.xml

...                                                                 3 / 3 (100%)

Time: 00:00.014, Memory: 10.00 MB

User (Hexlet\Package\Tests\User)
 ✔ Get name with one·child
 ✔ Get name with no·children
 ✔ Get name with two

OK (3 tests, 6 assertions)

Что будет при ошибке?

Если сделать ошибку в логике функции, например, возвращать квадрат вместо куба, то:

  • Обычный тест без провайдера просто покажет: ожидалось 8, получено 4. Но не скажет, какой был вход.
  • Тест с провайдером покажет: ожидалось 8, получено 4 при аргументе 2. Это сильно упрощает отладку.
....F

Time: 00:00.025, Memory: 10.00 MB

There was 1 failure:

1) Hexlet\Package\Tests\MathTest::testCube with data set #4 (65, 4)
Failed asserting that 64 matches expected 65.

/projects/hexlet-php-testing/tests/MathTest.php:15

Выводы

Data Provider — мощный и удобный инструмент, который делает тесты компактнее, понятнее и информативнее. Его стоит использовать во всех случаях, где одна и та же логика проверяется на разных входных данных.


Самостоятельная работа

Протестируйте с помощью Data Provider функцию calculateRectangleArea(int $length, int $width): int:

function calculateRectangleArea(int $length, int $width): int
{
    if ($length <= 0 || $width <= 0) {
        return null; // Invalid dimensions
    }
    return $length * $width;
}

Функция calculateRectangleArea() принимает два параметра: длину и ширину прямоугольника. Она проверяет, что оба параметра положительные, и возвращает площадь прямоугольника, вычисленную как произведение длины и ширины. Если один из параметров не является положительным числом, функция возвращает null, указывая на недопустимые размеры.


Дополнительные материалы

  1. Документация PHPUnit о Data Provider

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff