Hoy vamos a hablar de dataProviders en PHPUnit .
Un dataProvider es una función que permite definir un conjunto de casos para un mismo test. Son perfectos para hacer pruebas unitarias de valores límite. Veamos un ejemplo en código:
/**
* @dataProvider sum_samples
*/
public function test_sum(
$number1,
$number2,
$expectedSum
) {
$this->assertEquals(
$expectedSum,
$number1 + $number2
);
}
public static function sum_samples(
) {
return array(
array( 0, 0, 0 ),
array( 0, 1, 1 ),
array( 1, 0, 1 ),
array( 1, 1, 2 ),
array( 3, 4, 8 )
);
}
Observemos ciertas reglas a cumplir:
- Un test se asocia con un dataProvider mediante la anotación @dataProvider
- El método provider debe ser público y estático
- El método provider debe devolver un array de arrays o un Iterator sobre arrays; cada subarray es un caso de test y debe contener tantos elementos como parámetros tenga el test
Nombrar los casos de test
Si al array que devuelve el provider se le ponen claves, éstas son utilizadas en los mensajes de error en caso de que el test falle, por lo que puede ser útil “nombrar” los casos en estas claves. Por ejemplo, si el anterior provider lo escribimos así:
public static function sum_samples(
) {
return array(
"Null values" => array( 0, 0, 0 ),
"Null & not null" => array( 0, 1, 1 ),
"Not null & null" => array( 1, 0, 1 ),
"Not null values (a)" => array( 1, 1, 2 ),
"Not null values (b)" => array( 3, 4, 8 )
);
}
El resultado de pasar los tests será:
There was 1 failure:
1) SampleProviderTest::test_sum with data set "Not null values (b)" (3, 4, 8)
Failed asserting that <integer:7> matches expected <integer:8>.
Fijémonos que en este ejemplo he distinguido dos casos “Not null values (a)” y “Not null values (b)”. Es importante darse cuenta de que estamos usando las claves de un array asociativo para nombrar los casos, y que si escribimos dos claves iguales estaremos sobrescribiendo un caso (y lo peor, no nos daríamos cuenta, ya que el código funcionaría, pero ejecutaría 4 tests en lugar de 5).
¿Cuándo se ejecutan los providers?
Aquí va un consejo de esos que es mejor no tener que aprender de la propia experiencia: los dataProviders se ejecutan una sola vez, antes incluso que el setUpBeforeClass(). Puede parecer una tontería de consejo y de hecho lo es para casos tan sencillos como el ejemplo que hemos mostrado, pero aquí va un ejemplo algo más real:
class AppUserTest extends PHPUnit_Framework_TestCase {
private $app = null;
public static function setUpBeforeClass(
) {
self::$app = new App();
}
/**
* @dataProvider validData
*/
public function testUserCanLogin(
$validName,
$validPass
) {
$anUser = new AppUser($validName, $validPass);
$this->assertTrue($anUser->login());
}
public static function validData(
) {
$validData = array();
foreach (self::$app->getSampleValidUsers() as $user) {
$validData []= array(
$anUser->getName(),
$anUser->getPass()
);
}
return $validData;
}
}
¿Veis el problema?
Efectivamente, tenemos un provider con una dependencia inválida. Dicho provider necesita leer ciertos datos de una cierta entidad ‘global’ (self:$app), pero esta necesita ser inicializada en setUpBeforeClass(). Estamos ante una situación imposible: un provider no puede depender de nada inicializado en setUpBeforeClass(), porque se ejecuta antes que dicha función. Tenedlo en cuenta ;-)
Lo más ‘escalofriante’ es que en un conjunto de tests los providers se ejecutan antes que cualquier setUpBeforeClass(), aunque sea de otro test!
La siguiente prueba lo ilustra:
<?php
# File: PhpUnitOrderTest.php
class PhpUnitOrderTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass(
) {
echo "TestCase #1 - setUpBeforeClass()\n";
}
public static function tearDownAfterClass(
) {
echo "TestCase #1 - tearDownAfterClass()\n";
}
public function setUp(
) {
echo "TestCase #1 - setUp()\n";
}
public function tearDown(
) {
echo "TestCase #1 - tearDown()\n";
}
/**
* @dataProvider sampleProvider
*/
public function testSample(
$param
) {
echo "TestCase #1 - testSample()\n";
$this->assertTrue($param);
}
public static function sampleProvider(
) {
echo "TestCase #1 - sampleProvider()\n";
return array(
"#1" => array( 1 == 1 ),
"#2" => array( 2 != 1 ),
);
}
}
<?php
# File: OtherTest.php
class OtherTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass(
) {
echo "TestCase #2 - setUpBeforeClass()\n";
}
public static function tearDownAfterClass(
) {
echo "TestCase #2 - tearDownAfterClass()\n";
}
public function setUp(
) {
echo "TestCase #2 - setUp()\n";
}
public function tearDown(
) {
echo "TestCase #2 - tearDown()\n";
}
/**
* @dataProvider sampleProvider
*/
public function testSample(
$param
) {
echo "TestCase #2 - testSample()\n";
$this->assertTrue($param);
}
public static function sampleProvider(
) {
echo "TestCase #2 - sampleProvider()\n";
return array(
"#1" => array( 1 == 1 ),
"#2" => array( 2 != 1 ),
);
}
}
La salida de PHPUnit:
TestCase #2 - sampleProvider()
TestCase #1 - sampleProvider()
PHPUnit 3.5.15 by Sebastian Bergmann
.TestCase #2 - setUpBeforeClass()
TestCase #2 - setUp()
TestCase #2 - testSample()
TestCase #2 - tearDown()
.TestCase #2 - setUp()
TestCase #2 - testSample()
TestCase #2 - tearDown()
.TestCase #2 - tearDownAfterClass()
TestCase #1 - setUpBeforeClass()
TestCase #1 - setUp()
TestCase #1 - testSample()
TestCase #1 - tearDown()
.TestCase #1 - setUp()
TestCase #1 - testSample()
TestCase #1 - tearDown()
.TestCase #1 - tearDownAfterClass()
Time: 0 seconds, Memory: 3.50Mb
OK (4 tests, 4 assertions)
¿Os fijáis en los providers apareciendo antes incluso del mensaje inicial de PHPUnit? ;-)
Great content! Super high-quality! If you want to learn more about PHP, you can learn from here -
ResponderEliminarHow to display PDF file in PHP from database
How to read CSV file in PHP and store in MySQL
Create and Download Word document PHP
NodeJS Introduction
NodeJS REPL
NodeJS Data Types
NodeJS Events
NodeJS Callback