web-dev-qa-db-fra.com

Quel est le meilleur moyen de créer une usine pour les objets de test unitaire?

J'essaie d'apprendre le TDD et j'ai du mal à créer des usines pour des objets personnalisés. Par exemple, si j'ai un type d'utilisateur personnalisé et que tous les utilisateurs de ce type doivent avoir une capacité spécifique, il est fastidieux d'utiliser la fabrique WP_UnitTest pour créer un utilisateur, puis l'ajouter manuellement dans chaque test avant d'utiliser l'objet. Parce que j'ai besoin d'utiliser ces objets dans une variété de fichiers de test, il serait redondant d'avoir une fonction fabrique dans chaque fichier/classe de test, et il est fastidieux d'implémenter manuellement les quatre variantes des méthodes fabriques (create, create_and_get, create_many et create_and_get_many).

Quel est le meilleur moyen de créer une usine pour des objets de test unitaire comme celui-ci?

1
philosophyguy

Si je comprends correctement, ce que vous recherchez est une bonne bibliothèque pour créer stubs et mocks .

Si vous utilisez PHPUnit, il a des fonctionnalités pour cela. Voir https://phpunit.de/manual/current/en/test-doubles.html#test-doubles

Par exemple, supposons que vous ayez une classe comme celle-ci:

namespace MyApp;

class MyCustomUser {

   public function __construct(\WP_User $user) {
     $this->user = $user;
   }

   public function hasCapability($capability) {
      return $this->user->can($capability);
   }
}

Vous pouvez créer un trait qui contient une fabrique d'utilisateurs et utiliser la fonctionnalité moqueuse de PHPUnit:

namespace MyApp\Tests;

trait CreateUserTrait {

   protected function createUser(array $capabilitites) {

      $stub = $this->createMock(MyCustomUser::class);

      $has_capability = function($capability) use ($capabilitites) {
         return in_array($capability, $capabilitites, true);
      };

      $stub->method('hasCapability')->will($this->returnCallback($has_capability));
   }
}

À ce stade de vos classes de test, vous pouvez utiliser cette caractéristique et utiliser l’usine:

namespace MyApp\Tests;

use PHPUnit\Framework\TestCase;

class UserMockTest extends TestCase
{
    use CreateUserTrait;

    public function testUserFactoryWorks()
    {
        $userMock = $this->createUser(['create_post']);

        $this->assertTrue($userMock->hasCapability('create_post'));
    }
}

Bien sûr, les imitations et les moignons sont utiles lorsque vous devez tester otherobjects qui utilisent un objet imité. Vous ne ferez pas de simulation du SUT.

Un effet secondaire intéressant de l'utilisation de la maquette est que nous n'avons pas utilisé aucune classefonction WordPress dans le test (même si l'objet utilisateur d'origine utilise WordPress WP_User object), le test peut donc être exécuté sans charger WordPress, en faisant un réeltest unitaire): si vous chargez WordPress, il devient un test d'intégration, et non un test unitaire).

Beaucoup de gens trouvent la syntaxe moqueuse de PHP un peu difficile à digérer. Si vous êtes l'un d'entre eux, vous pouvez jeter un coup d'œil à Mockery , qui a une API plus simple. .

Selon vos besoins, Faker pourrait également être d'une grande aide.


Remarque: le code ci-dessus nécessite PHP 5.5 et la version de PHPUnit utilisée est la version 5. * qui nécessite PHP 5.6+

1
gmazzap

J'ai écrit un tutoriel sur la création de vos propres fabriques PHPUnit } _. Cependant, dans ce cas, vous devez simplement étendre la fabrique d'utilisateurs existante, WP_UnitTest_Factory_For_User. Si vous jetez un oeil à la source de cette classe , vous verrez que vous n'avez pas besoin d'implémenter un tas de méthodes différentes, juste une: create_object(). Cela sera utilisé par les autres méthodes que vous mentionnez.

Voici à quoi ressemble la méthode dans la fabrique d'utilisateurs:

function create_object( $args ) {
    return wp_insert_user( $args );
}

Assez simple, hein?

Il vous suffit donc de le modifier pour créer une classe enfant de cette fabrique, comme ceci:

class WP_UnitTest_Factory_For_User_With_Cap extends WP_UnitTest_Factory_For_User {

    function create_object( $args ) {
        $user_id = parent::create_object( $args );

        if ( $user_id ) {
            $user = new WP_User( $user_id );
            $user->add_cap( 'my_custom_cap' );
        }

        return $user_id;
    }
}

Ensuite, vous pouvez faire quelque chose comme ceci dans votre méthode setUp():

$this->factory->user_with_cap = new WP_UnitTest_Factory_For_User_With_Cap( $this->factory );

Désormais, dans vos tests, vous pouvez remplacer $this->factory->user par $this->factory->user_with_cap à chaque endroit où vous avez besoin de la création d'un utilisateur avec cette limite.


Une autre approche consisterait à créer votre propre classe de test abstraite personnalisée qui est un enfant de WP_UnitTestCase et à étendre tous vos testscases à partir de celle-ci. Ensuite, vous pouvez créer une méthode factory sur cette classe. Toutefois, cela ne fonctionnera que si tous vos tests peuvent être étendus à partir d'un seul parent, ce qui peut ne pas être le cas si vous avez des tests Ajax, par exemple.

1
J.D.