PHPUnit: Электронная таблица (spreadsheet) в качестве источника данных (data provider)

от автора

В документации 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);     } } 

Внимания заслуживают два момента (остальное, надеюсь, очевидно):

  1. $position = 2; — первая строка (нумерация начинается с 1) с данными, всё что до неё можно использовать для комментариев (см. пример ниже)
  2. $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: Данные

image

Шаг #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

ссылка на оригинал статьи http://habrahabr.ru/post/252489/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *