phpunitEmpezando con phpunit


Observaciones

Esta sección proporciona una descripción general de qué es phpunit y por qué un desarrollador puede querer usarlo.

También debe mencionar cualquier tema importante dentro de phpunit y vincular a los temas relacionados. Dado que la Documentación para phpunit es nueva, es posible que deba crear versiones iniciales de esos temas relacionados.

Versiones

Versión Finaliza el soporte Soportado en esas versiones de PHP Fecha de lanzamiento
5.4 2016-08-05 PHP 5.6, PHP 7. 2016-06-03
4.8 2017-02-03 PHP 5.3, PHP 5.4, PHP 5.5 y PHP 5.6. 2015-08-07

Crea la primera prueba PHPUnit para nuestra clase

Imagina que tenemos una clase Math.php con lógica de cálculo de fiobanacci y números factoriales. Algo como esto:

<?php    
class Math {
    public function fibonacci($n) {
        if (is_int($n) && $n > 0) {
            $elements = array();
            $elements[1] = 1;
            $elements[2] = 1;
            for ($i = 3; $i <= $n; $i++) {
                $elements[$i] = bcadd($elements[$i-1], $elements[$i-2]);
            }
            return $elements[$n];
        } else {
            throw new 
                InvalidArgumentException('You should pass integer greater than 0');
        }
    }

    public function factorial($n) {
        if (is_int($n) && $n >= 0) {
            $factorial = 1;
            for ($i = 2; $i <= $n; $i++) {
                $factorial *= $i;
            }
            return $factorial;
        } else {
            throw new 
                InvalidArgumentException('You should pass non-negative integer');
        }
    }
}
 

La prueba mas sencilla

Queremos probar la lógica de los métodos fibonacci y factorial . Vamos a crear el archivo MathTest.php en el mismo directorio con Math.php . En nuestro código podemos utilizar diferentes aserciones . El código más simple será algo como esto (solo usamos assertEquals y assertTrue ):

<?php
require 'Math.php';

use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase{
    public function testFibonacci() {
        $math = new Math();
        $this->assertEquals(34, $math->fibonacci(9));
    }

    public function testFactorial() {
        $math = new Math();
        $this->assertEquals(120, $math->factorial(5));
    }

    public function testFactorialGreaterThanFibonacci() {
        $math = new Math();
        $this->assertTrue($math->factorial(6) > $math->fibonacci(6));
    }
}
 

Podemos ejecutar esta prueba desde la consola con el comando phpunit MathTest y la salida será:

    PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 88 ms, Memory: 10.50Mb

OK (3 tests, 3 assertions)
 

Usando los proveedores de datos

Un método de prueba puede aceptar argumentos arbitrarios. Estos argumentos deben ser proporcionados por un método de proveedor de datos . El método del proveedor de datos que se utilizará se especifica mediante la anotación @dataProvider . :

<?php
require 'Math.php';

use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase {
    /**
     * test with data from dataProvider
     * @dataProvider providerFibonacci
     */
    public function testFibonacciWithDataProvider($n, $result) {
        $math = new Math();
        $this->assertEquals($result, $math->fibonacci($n));
    }

    public function providerFibonacci() {
        return array(
            array(1, 1),
            array(2, 1),
            array(3, 2),
            array(4, 3),
            array(5, 5),
            array(6, 8),
        );
    }
}
 

Podemos ejecutar esta prueba desde la consola con el comando phpunit MathTest y la salida será:

    PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

......                                                              6 / 6 (100%)

Time: 97 ms, Memory: 10.50Mb

OK (6 tests, 6 assertions)


<?php
require 'Math.php';
use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;
 

Excepciones de prueba

Podemos probar si el código bajo prueba lanza una excepción usando el método expectException() . También en este ejemplo, agregamos una prueba fallida para mostrar el resultado de la consola para las pruebas fallidas.

<?php
require 'Math.php';
use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase {
    public function testExceptionsForNegativeNumbers() {
        $this->expectException(InvalidArgumentException::class);
        $math = new Math();
            $math->fibonacci(-1);
    }

    public function testFailedForZero() {
        $this->expectException(InvalidArgumentException::class);
        $math = new Math();
        $math->factorial(0);
    }
}
 

Podemos ejecutar esta prueba desde la consola con el comando phpunit MathTest y la salida será:

        PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

.F                                                                  2 / 2 (100%)

Time: 114 ms, Memory: 10.50Mb

There was 1 failure:

1) MathTest::testFailedForZero
Failed asserting that exception of type "InvalidArgumentException" is thrown.

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
 

SetUp y TearDown

También PHPUnit admite compartir el código de configuración. Antes de ejecutar un método de prueba, se invoca un método de plantilla denominado setUp (). setUp() es donde creas los objetos contra los cuales setUp() . Una vez que el método de prueba ha terminado de ejecutarse, ya sea que haya tenido éxito o haya fallado, se invoca otro método de plantilla llamado tearDown() . tearDown() es donde limpia los objetos contra los que ha probado.

<?php
require 'Math.php';

use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase {
    public $fixtures;
    protected function setUp() {
        $this->fixtures = [];
    }

    protected function tearDown() {
        $this->fixtures = NULL;
    }

    public function testEmpty() {
        $this->assertTrue($this->fixtures == []);
    }
}
 

Más información

Hay muchas más grandes oportunidades de PHPUnit que puede utilizar en su prueba. Para más información ver en manual oficial.

Ejemplo de PHPUNIT con APItest usando Stub And Mock

Clase para la que creará un caso de prueba unitaria. class Authorization {

/* Observer so that mock object can work. */    
 public function attach(Curl $observer)
{
    $this->observers = $observer;
}

/* Method for which we will create test */
public  function postAuthorization($url, $method) {
    
    return $this->observers->callAPI($url, $method);
}
 

}

Ahora no queríamos ninguna interacción externa de nuestro código de prueba, por lo que necesitamos crear un objeto simulado para la función callAPI, ya que esta función en realidad está llamando a la URL externa a través de Curl. class AuthorizationTest extends PHPUnit_Framework_TestCase {

protected $Authorization;

/**
 * This method call every time before any method call.
 */
protected function setUp() {
    $this->Authorization = new Authorization();
}

/**
 * Test Login with invalid user credential
 */
function testFailedLogin() {

    /*creating mock object of Curl class which is having callAPI function*/
    $observer = $this->getMockBuilder('Curl')
                     ->setMethods(array('callAPI'))
                     ->getMock();

    /* setting the result to call API. Thus by default whenver call api is called via this function it will return invalid user message*/
    $observer->method('callAPI')
            ->will($this->returnValue('"Invalid user credentials"'));

    /* attach the observer/mock object so that our return value is used */
    $this->Authorization->attach($observer);

    /* finally making an assertion*/
    $this->assertEquals('"Invalid user credentials"',           $this->Authorization->postAuthorization('/authorizations', 'POST'));
}
 

}

A continuación se muestra el código para la clase de curl (solo una muestra) class Curl{ function callAPI($url, $method){

    //sending curl req
}
 

}

Ejemplo de prueba mínima

Dada una simple clase de PHP:

class Car
{
    private $speed = 0;

    public getSpeed() {
        return $this->speed;
    }

    public function accelerate($howMuch) {
        $this->speed += $howMuch;
    }
}
 

Puede escribir una prueba PHPUnit que pruebe el comportamiento de la clase bajo prueba llamando a los métodos públicos y verifique si funcionan como se espera:

class CarTest extends PHPUnit_Framework_TestCase
{
    public function testThatInitalSpeedIsZero() {
        $car = new Car();
        $this->assertSame(0, $car->getSpeed());
    }

    public function testThatItAccelerates() {
        $car = new Car();
        $car->accelerate(20);
        $this->assertSame(20, $car->getSpeed());
    }

    public function testThatSpeedSumsUp() {
        $car = new Car();
        $car->accelerate(30);
        $car->accelerate(50);
        $this->assertSame(80, $car->getSpeed());
    }
}
 

Partes importantes:

  1. La clase de prueba debe derivarse de PHPUnit_Framework_TestCase.
  2. Cada nombre de función de prueba debe comenzar con el prefijo 'prueba'
  3. Use las funciones $this->assert... para verificar los valores esperados.

Clases de burla

La práctica de reemplazar un objeto con un doble de prueba que verifique las expectativas, por ejemplo, afirmar que se ha llamado a un método, se conoce como burlón.

Asumamos que tenemos SomeService para probar.

class SomeService
{
    private $repository;
    public function __construct(Repository $repository)
    {
        $this->repository = $repository;
    }

    public function methodToTest()
    {
        $this->repository->save('somedata');
    }
}   
 

Y queremos probar si methodToTest realmente llama al método de save del repositorio. Pero no queremos en realidad crear una instancia del repositorio (o quizás Repository es solo una interfaz).

En este caso podemos simular Repository .

use PHPUnit\Framework\TestCase as TestCase;

class SomeServiceTest extends TestCase
{    
    /**
     * @test
     */
    public function testItShouldCallRepositorySavemethod()
    {
        // create an actual mock
        $repositoryMock = $this->createMock(Repository::class);

        $repositoryMock->expects($this->once()) // test if method is called only once
             ->method('save')                   // and method name is 'save'
             ->with('somedata');                // and it is called with 'somedata' as a parameter

        $service = new SomeService($repositoryMock);
        $service->someMethod();
    }
}