В документации PHPUnit есть небольшой раздел посвященный источникам данных (data provider), которые позволяют скормить тесту большой объем данных, а чуть ниже есть даже пример источника данных для CSV файла.
Конечно же, использовать полноценную электронную таблицу (spreadsheet)!
Договоримся что:
- Для каждого файла-теста существует свой файл с данными (всего один, вместо кучи CSV файлов)
- Данные внутри файла хранятся на отдельных страницах по одной для каждого теста, название страницы совпадает с названием теста (то ради чего все и затевалось)
И сразу перейдем к делу (минимум текста, максимум кода), весь код также доступен по ссылке внизу заметки:
Шаг #1: Зависимости
Нам понадобится (composer.json
):
{
"name": "PHPUnitSpreadsheetDataProvider",
"require": {
"php": ">=5.5.0",
"phpunit/phpunit": "4.5.*",
"phpoffice/phpexcel": "1.8.*"
},
"autoload": {
"classmap": [
"src/"
]
}
}
Шаг #2: Основная логика
Всё сводится к тому чтобы добавить собственный метод который будет получать данные из файла и превращать их в источник данных для тестов.
Получение:
<?php
// PHPUnitTestCase.php
trait PHPUnitSDP_PHPUnitTestCase {
/**
* @var PHPExcel_Reader_IReader|PHPExcel_Reader_Abstract
*/
private $_reader;
/**
* Возвращает пусть к ресурсу для теста.
*
* @param string $resource
*
* @return string
*/
protected function getTestResource($resource = null) {
$path = (new ReflectionClass($this))->getFileName();
$dirname = pathinfo($path, PATHINFO_DIRNAME);
$filename = pathinfo($path, PATHINFO_FILENAME);
$resource = $resource ?: 'xml';
return "{$dirname}/{$filename}.{$resource}";
}
/**
* Возвращает данные для теста.
*
* @param string $test
*
* @return array|Iterator
*/
public function getTestDataProvider($test) {
// Файл
$position = 2;
$resource = $this->getTestResource('data.ods');
// Reader?
if (is_null($this->_reader)) {
$this->_reader = PHPExcel_IOFactory::createReaderForFile($resource);
}
// Настройки
$this->_reader->setReadDataOnly(true);
$this->_reader->setLoadSheetsOnly($test);
// Данные
return new PHPUnitSDP_PHPExcelWorksheetRowIterator(
$this->_reader->load($resource)->getActiveSheet(), $position);
}
}
Внимания заслуживают два момента (остальное, надеюсь, очевидно):
$position = 2;
— первая строка (нумерация начинается с 1) с данными, всё что до неё можно использовать для комментариев (см. пример ниже)$resource
— определяет название файла с данными, в данном случае это "ИмяТеста.data.ods
"
Превращение:
<?php
// PHPExcelWorksheetRowIterator.php
class PHPUnitSDP_PHPExcelWorksheetRowIterator extends PHPExcel_Worksheet_RowIterator {
/**
* @return array
*/
public function current() {
$current = array();
foreach (parent::current()->getCellIterator() as $cell) {
/* @var $cell PHPExcel_Cell */
$current[] = $this->getValue($cell->getCalculatedValue());
}
return $current;
}
/**
* @param mixed $value
*
* @return mixed
*/
protected function getValue($value) {
switch (mb_strtolower(trim($value))) {
case 'null':
$value = null;
break;
case 'true':
$value = true;
break;
case 'false':
$value = false;
break;
default:
/* empty */
break;
}
return $value;
}
}
Из особенностей стоит отметить возможность использования формул в ячейках, но от отдельного метода для конвертации значений отказаться все равно не получится — во-первых не для всех типов данных есть необходимые функции (тот же NULL
), во-вторых вычисление формул требует времени и ресурсов.
Шаг #3: Данные
Шаг #4: Тест
// SDPTest.php
class SDPTest extends PHPUnit_Framework_TestCase {
use PHPUnitSDP_PHPUnitTestCase;
/**
* @dataProvider getTestDataProvider
*
* @param number $base
* @param number $exp
* @param number $expected
*
* @return void
*/
public function testPow($base, $exp, $expected) {
$this->assertEquals($expected, pow($base, $exp));
}
/**
* @dataProvider getTestDataProvider
*
* @param number $arg
* @param number $expected
*
* @return void
*/
public function testSqrt($arg, $expected) {
$this->assertEquals($expected, sqrt($arg));
}
}
Вся магия заключена в аннотации @dataProvider getTestDataProvider
— перед запуском теста PHPUnit вызовет определенный ранее метод PHPUnitSDP_PHPUnitTestCase::getTestDataProvider()
с аргументом в котором содержится название теста и получит необходимый источник данных.
Шаг #5: Результат
PHPUnitSpreadsheetDataProvider> phpunit
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.
Configuration read from PHPUnitSpreadsheetDataProvider/phpunit.xml
......F
Time: 158 ms, Memory: 8.75Mb
There was 1 failure:
1) SDPTest::testSqrt with data set #4 (4.0, 3.0)
Failed asserting that 2.0 matches expected 3.0.
PHPUnitSpreadsheetDataProvider/tests/SDPTest.php:28
phar://PHPUnitSpreadsheetDataProvider/phpunit.phar/phpunit/TextUI/Command.php:152
phar://PHPUnitSpreadsheetDataProvider/phpunit.phar/phpunit/TextUI/Command.php:104
FAILURES!
Tests: 7, Assertions: 7, Failures: 1.
Заключение
Надеюсь данный рецепт кому-нибудь пригодится :)
Проект: yadi.sk/d/AyegnPCqf7i9Y
Автор: LastDragon