PHP Fournisseurs de données PHPUnit


Exemple

Les méthodes de test nécessitent souvent de tester des données. Pour tester complètement certaines méthodes, vous devez fournir différents jeux de données pour chaque condition de test possible. Bien sûr, vous pouvez le faire manuellement en utilisant des boucles, comme ceci:

...
public function testSomething()
{
    $data = [...];
    foreach($data as $dataSet) {
       $this->assertSomething($dataSet);
    }
}
... 

Et quelqu'un peut le trouver commode. Mais cette approche présente certains inconvénients. Tout d'abord, vous devrez effectuer des actions supplémentaires pour extraire des données si votre fonction de test accepte plusieurs paramètres. Deuxièmement, en cas d'échec, il serait difficile de distinguer l'ensemble de données défaillant sans messages ni débogage supplémentaires. Troisièmement, PHPUnit fournit un moyen automatique de traiter les ensembles de données de test à l'aide de fournisseurs de données .

Le fournisseur de données est une fonction qui doit renvoyer des données pour votre scénario de test particulier.

Une méthode de fournisseur de données doit être publique et renvoyer un tableau de tableaux ou un objet qui implémente l'interface Iterator et génère un tableau pour chaque étape d'itération. Pour chaque tableau faisant partie de la collection, la méthode de test sera appelée avec le contenu du tableau comme arguments.

Pour utiliser un fournisseur de données avec votre test, utilisez l'annotation @dataProvider avec le nom de la fonction de fournisseur de données spécifiée:

/**
* @dataProvider dataProviderForTest
*/
public function testEquals($a, $b)
{
    $this->assertEquals($a, $b);
}

public function dataProviderForTest()
{
    return [
        [1,1],
        [2,2],
        [3,2] //this will fail
    ];
}

Tableau de tableaux

Notez que dataProviderForTest() renvoie un tableau de tableaux. Chaque tableau imbriqué a deux éléments et remplira les paramètres nécessaires pour testEquals() un par un. Une erreur comme celle-ci sera Missing argument 2 for Test::testEquals() s'il n'y a pas assez d'éléments. PHPUnit va automatiquement parcourir les données et exécuter les tests:

public function dataProviderForTest()
{
    return [
        [1,1], // [0] testEquals($a = 1, $b = 1)
        [2,2], // [1] testEquals($a = 2, $b = 2)
        [3,2]  // [2] There was 1 failure: 1) Test::testEquals with data set #2 (3, 4)
    ];
}

Chaque ensemble de données peut être nommé pour plus de commodité. Il sera plus facile de détecter les données défaillantes:

public function dataProviderForTest()
{
    return [
        'Test 1' => [1,1], // [0] testEquals($a = 1, $b = 1)
        'Test 2' => [2,2], // [1] testEquals($a = 2, $b = 2)
        'Test 3' => [3,2]  // [2] There was 1 failure: 
                           //     1) Test::testEquals with data set "Test 3" (3, 4)
    ];
}

Les itérateurs

class MyIterator implements Iterator {
    protected $array = [];

    public function __construct($array) {
        $this->array = $array;
    }

    function rewind() {
        return reset($this->array);
    }

    function current() {
        return current($this->array);
    }

    function key() {
        return key($this->array);
    }

    function next() {
        return next($this->array);
    }

    function valid() {
        return key($this->array) !== null;
    }
}
...

class Test extends TestCase
{
    /**
     * @dataProvider dataProviderForTest
     */
    public function testEquals($a)
    {
        $toCompare = 0;

        $this->assertEquals($a, $toCompare);
    }

    public function dataProviderForTest()
    {
        return new MyIterator([
            'Test 1' => [0],
            'Test 2' => [false],
            'Test 3' => [null]
        ]);
    }
}

Comme vous pouvez le voir, un itérateur simple fonctionne également.

Notez que même pour un seul paramètre, le fournisseur de données doit retourner un tableau [$parameter]

Parce que si nous changeons notre méthode current() (qui retourne des données à chaque itération) à ceci:

function current() {
    return current($this->array)[0];
}

Ou changer les données réelles:

return new MyIterator([
            'Test 1' => 0,
            'Test 2' => false,
            'Test 3' => null
        ]);

Nous aurons une erreur:

There was 1 warning:

1) Warning
The data provider specified for Test::testEquals is invalid.

Bien sûr, il n'est pas utile d'utiliser un objet Iterator sur un tableau simple. Il devrait implémenter une logique spécifique pour votre cas.

Générateurs

Il n'est pas explicitement noté et affiché dans le manuel, mais vous pouvez également utiliser un générateur en tant que fournisseur de données. Notez que la classe Generator implémente l'interface Iterator .

Voici donc un exemple d'utilisation de DirectoryIterator combiné à un generator :

/**
 * @param string $file
 *
 * @dataProvider fileDataProvider
 */
public function testSomethingWithFiles($fileName)
{
    //$fileName is available here
    
    //do test here
}

public function fileDataProvider()
{
    $directory = new DirectoryIterator('path-to-the-directory');

    foreach ($directory as $file) {
        if ($file->isFile() && $file->isReadable()) {
            yield [$file->getPathname()]; // invoke generator here.
        }
    }
}

Notez que le fournisseur yield un tableau. Vous recevrez plutôt un avertissement de fournisseur de données non valide.