
Поставщики (провайдеры) данных.
Поставщики данных (data providers) используются для подстановки аргументов в тестируемый метод, для вызова одного и того же метода с разными значениями. Это позволяет не дублировать один и тот же метод с разными данными.Провайдеры данных, содержат в себе массив параметров, которые будут подставлены во время запуска теста.
Провайдер данных это обычный метод, который возвращает массив с набором параметров следующем формате:
public function providerName(){ return [ [12, 12], [0, 0], [true, 'str'] ]; }
Чтобы использовать провайдер, нужно перед методом, в который будем вставлять данные, разместить комментарий с директивой @dataProvider:
/** * @dataProvider nameMethodProvider */После первого слэша обязательно две звездочки.
Где nameMethodProvider – название метода провайдера.
Например:
/** * @dataProvider providerSum */ public function testParseAndSum($a, $b) { $result = SimpleParser::ParseAndSum($b); $this->assertEquals($a, $result); } public function providerSum(){ return [ [12, 12], [0, 0], [true, 'str'] ]; }где в метод testParseAndSum передаем из провайдера:
$a – ожидаемое значение;
$b – значение полученное при выполнении тестируемого метода
В примере метод assertEquals() сравнивает два значения. Таким образом имеем 3 теста, где сравниваются 12 с 12, 0 с 0 и true с 'str'. Последний тест не пройдет, т.к.
true - это логический тип данных, а 'str' - строка.
Рассмотрим пример, который в т.ч. использует провайдер данных.
Файл app\Connection.php
<?php namespace app; /* * Класс устанавливающий соединение с заданным сайтом используя библиотеку curl * В случае успеха возвращает true, иначе false. */ class Connection { public $curl; public function __construct() { $this->curl = curl_init(); } public function siteVerification($host) { curl_setopt($this->curl, CURLOPT_URL, $host); curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($this->curl); if ($response) return true; else return false; } public function curlClose(){ curl_close($this->curl); } }
Можете запустить в WEB данный код, проверить его работу:
$con = new Connection(); $host = 'http://klisl.com'; $con->siteVerification($host); $con->curlClose();Файл tests\ConnectionTest.php
<?php namespace tests; use PHPUnit\Framework\TestCase; use app\Connection; class ConnectionTest extends TestCase { protected $obj; //объект тестируемого класса /* * Метод вызывается в начале тестирования * Тут создаем объект класса который будем тестировать * при его создании выполняется метод-конструктор прописанный в данном классе */ protected function setUp() { $this->obj = new Connection(); } /** * @dataProvider hostsProvider * Тест проходит успешно если возвращаемое значение равно true * Проверяем на заведомо существующих доменах. * Используется метод провайдер данных (hostsProvider) для получения аргумента $a */ public function testSiteVerificationTrue($a){ $this->assertTrue($this->obj->siteVerification($a)); } /* * Метод предоставляющий данные для тестирования. * Для каждого массива будет произведен отдельный тест */ public function hostsProvider(){ return [ ['ukr.net'], ['klisl.com'], ]; } /* * Тест проходит успешно если возвращаемое значение равно false * Тестируем с несуществующим доменом */ public function testSiteVerificationFalse(){ $this->assertFalse($this->obj->siteVerification('cocojambo')); } }
Запускаем тестирование в консоли:
phpunit testsили другой командой в зависимости от настроек которые мы рассматривали в предыдущей части статьи.
Результат:

Зависимости между методами тестирующего класса.
Использование зависимостей позволяет пропускать выполнение определенных методов тестирующего класса в случае если методы тестов от которых они зависимы не выполнены успешно, а так же передавать нужные данные из одного метода в другой. Для этого используется директива @dependsВ первую очередь выполняется родительский метод от которого зависит выполняемый тест. Если родительский тест не будет пройден, то зависимый метод теста не будет выполнен.
Пример.
<?php namespace tests; use PHPUnit\Framework\TestCase; class MyTest extends TestCase { public function testOne(){ $num = 3; $this->assertEquals(3, $num); return $num; } /** * @depends testOne */ public function testTwo($num){ $result = 10 + $num; $this->assertEquals(13,$result); } }Первый тест (метод testOne) будет пройден успешно, т.к. метод assertEquals сравнивает два переданных ему аргумента между собой, а в нашем случае 3 = 3.
Далее передаем значение переменной $num в метод testTwo, для этого дописываем
return $num;Т.к. родительский тест пройден, будет выполнен второй, зависимый тест метода testTwo, который так же завершится успешно. А вот если в первом тесте вы поменяете значение переменной:
$num = 4;то увидим, что второй тест был пропущен (S), т.к. родительский тест не пройден:

Тестирование защищенных и частных методов.
Стоит сразу отметить, что защищенные и частные методы далеко не всегда требуют тестирования.
Например есть такой класс и защищенный метод setPassword(), который нужно проверить:
<?php namespace main; class User { private $password; public function getPassword(){ if (isset($this->password)){ return $this->password; } else { return null; } } public function setPassword($login) { if(is_string($login)){ $this->password = $this->cryptPassword($login); return true; } return false; } protected function cryptPassword($login){ return md5(uniqid($login)); } }
Попробуем создать и выполнить тест (проверка на тип возвращаемого значения) обычным образом:
<?php namespace tests; use main\User; class UserTest extends TestCase { public function testGetPassword() { $obj = new User(); $login = 'login'; $this->assertInternalType('string', $obj->cryptPassword($login)); } }
Если тестируемый метод является защищенным или частным, то при выполнения теста получим ошибку такого плана:
Fatal error: Call to protected method User::cryptPassword() from context 'UserTest'…
т.к. данный защищенный метод может использоваться только внутри данного или дочерних классов.
Есть несколько вариантов тестирования защищенных (частных) методов.
1. Использование публичного метода взаимодействующего с защищенным (частным).
Из данного примера видно, что защищенный метод cryptPassword() вызывается в публичном методе setPassword(). Соответственно протестировав данный публичный метод на возвращаемое значение true можно быть уверенным, что метод cryptPassword() отработал и вернул некий пароль:
<?php namespace tests; use main\User; class UserTest extends TestCase { public function testGetPassword() { $obj = new User(); $login = 'login'; $this->assertTrue($obj->setPassword($login)); } }
Если нас не устраивает определение «некий» пароль и нужно, например, уточнить, что это строка длинной 32 символа, то у нас есть так же другой публичный метод getPassword(), с помощью которого и получим результат отработки метода cryptPassword().
<?php namespace unit; use main\User; class UserTest extends TestCase { public function testGetPassword() { $obj = new User(); $login = 'login'; $obj->setPassword($login); $this->assertEquals(32, strlen($obj->getPassword())); } }тут сначала выполняем метод setPassword() для формирования пароля и сохранения в свойстве $password, откуда метод getPassword() его и возьмет.
2. Использование интерфейса Reflection API.
В данном случае используем ReflectionClass - это специальный класс языка PHP, который содержит множество различных методов по получению информации о структуре произвольного класса. И что в данном случае особенно актуально – позволяет взаимодействовать с защищенными и частными методами.
<?php namespace tests; use main\User; class UserTest extends \PHPUnit_Framework_TestCase{ /** * Выполнение защищенного/частного метода класса. * * @param object &$object Объект тестируемого класса * @param string $methodName Вызываемое имя метода * @param array $parameters Массив параметров (аргументов) метода. * * @return mixed Возврат метода. */ public function invokeMethod(&$object, $methodName, array $parameters = array()) { $reflection = new \ReflectionClass(get_class($object)); $method = $reflection->getMethod($methodName); $method->setAccessible(true); return $method->invokeArgs($object, $parameters); } public function testCryptPassword(){ $obj = new User(); $login = 'login'; $password = $this->invokeMethod($obj, 'cryptPassword', [$login]); $this->assertEquals(32, strlen($password)); } }В данном случае мы напрямую проверяем, что строка возвращаемая защищенным методом cryptPassword() содержит 32 символа.
Данная статья является одной из серии статей про фреймворк для тестирования PHPUnit. Читайте так же:
- Установка PHPUnit.
- Основы PHPUnit - 1 часть.
- PHPUnit - тестирование исключений, анализ покрытия кода тестами.
- PHPUnit - создание и использование имитирующих объектов (mock), заглушек.