phpunitDémarrer avec phpunit


Remarques

Cette section fournit une vue d'ensemble de ce qu'est phpunit et pourquoi un développeur peut vouloir l'utiliser.

Il devrait également mentionner tout sujet important dans phpunit, et établir un lien avec les sujets connexes. La documentation de phpunit étant nouvelle, vous devrez peut-être créer des versions initiales de ces rubriques connexes.

Versions

Version Fin du support Pris en charge dans ces versions de PHP Date de sortie
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 et PHP 5.6. 2015-08-07

Créer le premier test PHPUnit pour notre classe

Imaginons que nous ayons une classe Math.php avec une logique de calcul du fiobanacci et des nombres factoriels. Quelque chose comme ça:

<?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');
        }
    }
}
 

Le test le plus simple

Nous voulons tester la logique des méthodes fibonacci et factorial . Créons le fichier MathTest.php dans le même répertoire avec Math.php . Dans notre code, nous pouvons utiliser différentes assertions . Le code le plus simple sera quelque chose comme ça (nous utilisons uniquement assertEquals et 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));
    }
}
 

Nous pouvons exécuter ce test depuis la console avec la commande phpunit MathTest et la sortie sera:

    PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 88 ms, Memory: 10.50Mb

OK (3 tests, 3 assertions)
 

Utilisation de dataProviders

Une méthode de test peut accepter des arguments arbitraires. Ces arguments doivent être fournis par une méthode de fournisseur de données . La méthode du fournisseur de données à utiliser est spécifiée à l'aide de l'annotation @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),
        );
    }
}
 

Nous pouvons exécuter ce test depuis la console avec la commande phpunit MathTest et la sortie sera:

    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;
 

Test des exceptions

Nous pouvons tester si une exception est levée par le code sous test en utilisant la méthode expectException() . Dans cet exemple, nous avons également ajouté un test échoué pour afficher la sortie de la console pour les tests ayant échoué.

<?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);
    }
}
 

Nous pouvons exécuter ce test depuis la console avec la commande phpunit MathTest et la sortie sera:

        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 et TearDown

Aussi PHPUnit prend en charge le partage du code d'installation. Avant qu'une méthode de test ne soit exécutée, une méthode de modèle appelée setUp () est appelée. setUp() est l'endroit où vous créez les objets contre lesquels vous allez tester. Une fois la méthode de test terminée, qu'elle ait réussi ou échoué, une autre méthode de modèle appelée tearDown() est appelée. tearDown() est l'endroit où vous nettoyez les objets contre lesquels vous avez testé.

<?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 == []);
    }
}
 

Plus d'informations

Il y a beaucoup plus d'opportunités de PHPUnit que vous pouvez utiliser dans votre test. Pour plus d'infos voir en manuel

Exemple de PHPUNIT avec APItest utilisant Stub And Mock

Classe pour laquelle vous allez créer un test élémentaire. 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);
}
 

}

Maintenant, nous ne voulions aucune interaction externe de notre code de test, nous devons donc créer un objet simulé pour la fonction callAPI car cette fonction appelle en réalité une URL externe via 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'));
}
 

}

Voici le code de la classe curl (juste un exemple) de la class Curl{ function callAPI($url, $method){

    //sending curl req
}
 

}

Exemple de test minimal

Étant donné une classe PHP simple:

class Car
{
    private $speed = 0;

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

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

Vous pouvez écrire un test PHPUnit pour tester le comportement de la classe testée en appelant les méthodes publiques et vérifier si elles fonctionnent comme prévu:

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());
    }
}
 

Pièces importantes:

  1. La classe de test doit dériver de PHPUnit_Framework_TestCase.
  2. Chaque nom de fonction de test doit commencer par le préfixe "test"
  3. Utilisez $this->assert... pour vérifier les valeurs attendues.

Cours de moqueur

La pratique consistant à remplacer un objet par un double de test qui vérifie les attentes, par exemple en affirmant qu'une méthode a été appelée, est appelée moqueur.

Supposons que nous avons SomeService à tester.

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

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

Et nous voulons tester si methodToTest appelle vraiment la méthode save du dépôt. Mais nous ne voulons pas réellement instancier un référentiel (ou peut-être que Repository est juste une interface).

Dans ce cas, nous pouvons simuler un 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();
    }
}