Je développe un plugin utilisant TDD et une chose que je ne parviens pas à tester est ... les hooks.
Je veux dire OK, je peux tester le rappel de hook, mais comment puis-je tester si un hook se déclenche réellement (les hooks personnalisés et les hooks par défaut de WordPress)? Je suppose que quelques moqueries aideront, mais je ne peux tout simplement pas comprendre ce qui me manque.
J'ai installé la suite de tests avec WP-CLI. Selon cette réponse , init
hook devrait se déclencher, mais ... ce n'est pas le cas; De plus, le code fonctionne dans WordPress.
De ma compréhension, le bootstrap est chargé en dernier, il est donc logique de ne pas déclencher init, la question qui reste est donc: comment diable devrais-je tester si des hooks sont déclenchés?
Merci!
Le fichier de démarrage ressemble à ceci:
$_tests_dir = getenv('WP_TESTS_DIR');
if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';
require_once $_tests_dir . '/includes/functions.php';
function _manually_load_plugin() {
require dirname( __FILE__ ) . '/../includes/RegisterCustomPostType.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
require $_tests_dir . '/includes/bootstrap.php';
Le fichier testé ressemble à ceci:
class RegisterCustomPostType {
function __construct()
{
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type()
{
register_post_type( 'foo' );
}
}
Et le test lui-même:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation()
{
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
Merci!
la source
phpunit
, pouvez-vous voir des tests ayant échoué ou réussi? Avez-vous installébin/install-wp-tests.sh
?RegisterCustomPostType::__construct()
n'est jamais appelé lorsque le plug-in est chargé pour les tests. Il est également possible que vous soyez affecté par le bogue n ° 29827 ; essayez peut-être de mettre à jour votre version de la suite de tests unitaires de WP.bin/install-wp-tests.sh
(depuis que j'ai utilisé wp-cli) @JD: RegisterCustomPostType :: __ construct est appelé (vient d'ajouter unedie()
déclaration et phpunit s'arrête là)did_action()
pour vérifier si des actions ont été déclenchées.init
hook).Réponses:
Test en isolation
Lors du développement d'un plugin, le meilleur moyen de le tester est de ne pas charger l'environnement WordPress.
Si vous écrivez du code qui peut être facilement testé sans WordPress, votre code devient meilleur .
Chaque composant testé par unité doit être testé séparément : lorsque vous testez une classe, vous devez uniquement tester cette classe spécifique, en supposant que tout le code fonctionne parfaitement.
C'est la raison pour laquelle les tests unitaires sont appelés "unit".
Un avantage supplémentaire, sans chargement de noyau, votre test s'exécutera beaucoup plus rapidement.
Éviter les crochets dans le constructeur
Un conseil que je peux vous donner est d'éviter de mettre des crochets dans les constructeurs. C'est l'une des choses qui rendra votre code testable de manière isolée.
Voyons le code de test dans OP:
Et supposons que ce test échoue . Qui est le coupable ?
Comment cela peut-il être amélioré?
Supposons que votre code de classe est:
(Remarque: je vais me référer à cette version de la classe pour le reste de la réponse.)
La façon dont j'ai écrit cette classe vous permet de créer des instances de la classe sans appeler
add_action
.Dans la classe ci-dessus, il y a 2 choses à tester:
init
en fait en luiadd_action
passant des arguments appropriésregister_post_type
réellement laregister_post_type
fonctionJe n'ai pas dit que vous deviez vérifier si le type de message existe: si vous ajoutez l'action appropriée et si vous appelez
register_post_type
, le type de message personnalisé doit exister: s'il n'existe pas, il s'agit d'un problème de WordPress.Rappelez-vous: lorsque vous testez votre plugin, vous devez tester votre code, pas le code WordPress. Dans vos tests, vous devez supposer que WordPress (comme toute autre bibliothèque externe que vous utilisez) fonctionne bien. C'est le sens du test unitaire .
Mais ... en pratique?
Si WordPress n'est pas chargé, si vous essayez d'appeler les méthodes de classe ci-dessus, vous obtenez une erreur irrécupérable. Vous devez donc vous moquer des fonctions.
La méthode "manuelle"
Bien sûr, vous pouvez écrire votre bibliothèque moqueuse ou "manuellement" simuler chaque méthode. C'est possible. Je vais vous dire comment faire cela, mais ensuite je vais vous montrer une méthode plus facile.
Si WordPress n'est pas chargé pendant l'exécution des tests, cela signifie que vous pouvez redéfinir ses fonctions, par exemple
add_action
ouregister_post_type
.Supposons que vous avez un fichier, chargé à partir de votre fichier d'amorçage, où vous avez:
J'ai réécrit les fonctions pour simplement ajouter un élément à un tableau global à chaque appel.
Vous devez maintenant créer (si vous n'en avez pas déjà) votre propre classe de cas de test de base étendue
PHPUnit_Framework_TestCase
: cela vous permet de configurer facilement vos tests.Cela peut être quelque chose comme:
De cette manière, avant chaque test, le compteur global est réinitialisé.
Et maintenant, votre code de test (je me réfère à la classe réécrite que j'ai postée ci-dessus):
Vous devriez noter:
Bien .. mais c'est un pita!
Oui, si vous devez vous moquer manuellement de toutes les fonctions de WordPress, c'est vraiment pénible. Un conseil général que je peux vous donner est d'utiliser le moins de fonctions possible de WP: vous n'avez pas à réécrire WordPress, mais des fonctions abstraites de WP que vous utilisez dans des classes personnalisées, afin de pouvoir les simuler et les tester facilement.
Par exemple, concernant l'exemple ci-dessus, vous pouvez écrire une classe qui enregistre des types de publication en faisant appel
register_post_type
à 'init' avec des arguments donnés. Avec cette abstraction, vous devez toujours tester cette classe, mais à d'autres endroits de votre code qui enregistrent des types de publication, vous pouvez utiliser cette classe en la moquant dans des tests (en supposant que cela fonctionne).Ce qui est génial, c’est que si vous écrivez une classe qui résume l’enregistrement CPT, vous pouvez créer un référentiel distinct et, grâce à des outils modernes comme Composer, l’ incorporer dans tous les projets pour lesquels vous en avez besoin: testez une fois, utilisez-le partout . Et si jamais vous rencontrez un bogue, vous pouvez le réparer en un seul endroit et avec un simple,
composer update
tous les projets où il est utilisé sont également corrigés.Pour la deuxième fois: écrire du code qui peut être testé isolément signifie écrire un meilleur code.
Mais tôt ou tard, j'ai besoin d'utiliser les fonctions de WP quelque part ...
Bien sûr. Vous ne devriez jamais agir parallèlement au noyau, cela n'a aucun sens. Vous pouvez écrire des classes qui encapsulent des fonctions WP, mais ces classes doivent également être testées. La méthode "manuelle" décrite ci-dessus peut être utilisée pour des tâches très simples, mais quand une classe contient beaucoup de fonctions WP, cela peut être pénible.
Heureusement, là-bas, il y a de bonnes personnes qui écrivent de bonnes choses. 10up , une des plus grandes agences WP, maintient une très bonne bibliothèque pour ceux qui veulent tester les plugins de la bonne façon. C'est
WP_Mock
.Il vous permet de simuler des fonctions WP et des hooks . En supposant que vous ayez chargé dans vos tests (voir le fichier repo), le même test que j'ai écrit ci-dessus devient:
Simple, n'est ce pas? Cette réponse n’est pas un tutoriel pour
WP_Mock
, alors lisez le fichier lisez-moi pour plus d’informations, mais l’exemple ci-dessus devrait être assez clair, je pense.De plus, vous n'avez pas besoin d'écrire vous-même
add_action
ouregister_post_type
de vous moquer , ni de maintenir des variables globales.Et des cours WP?
WP a aussi des classes, et si WordPress n’est pas chargé lorsque vous exécutez des tests, vous devez vous en moquer.
C'est beaucoup plus facile que de se moquer de fonctions, PHPUnit a un système embarqué pour se moquer d'objets, mais je veux ici vous suggérer Mockery . C'est une bibliothèque très puissante et très facile à utiliser. De plus, c'est une dépendance de
WP_Mock
, donc si vous l'avez, vous avez aussi Mockery.Mais qu'en est-il
WP_UnitTestCase
?La suite de tests WordPress a été créée pour tester le noyau de WordPress , et si vous souhaitez contribuer au noyau, elle est essentielle, mais son utilisation pour les plugins ne vous permet pas de tester en vase clos.
Regardez vers le monde WP: il existe de nombreux frameworks PHP modernes et CMS, et aucun d’entre eux ne suggère de tester des plugins / modules / extensions (ou peu importe leur nom) avec du code framework.
Si vous manquez des usines, une fonctionnalité utile de la suite, vous devez savoir qu'il existe des choses incroyables là-bas.
Les pièges et les inconvénients
Il existe un cas où le flux de travail que j'ai suggéré ici manque: les tests de base de données personnalisés .
En fait, si vous utilisez des tables de WordPress et fonctions standard pour y écrire (au plus bas niveau des
$wpdb
méthodes) vous ne devez jamais réellement l' écriture de données ou de test si les données sont en fait dans la base de données, assurez - vous simplement que les méthodes appropriées sont appelées avec des arguments appropriés.Cependant, vous pouvez écrire des plugins avec des tables et des fonctions personnalisées qui construisent des requêtes pour y écrire, et tester si ces requêtes fonctionnent, cela relève de votre responsabilité.
Dans ces cas, la suite de tests WordPress peut vous aider beaucoup, et le chargement de WordPress peut être nécessaire dans certains cas pour exécuter des fonctions telles que
dbDelta
.(Il n'y a pas besoin de dire d'utiliser une autre base de données pour les tests, n'est-ce pas?)
Heureusement, PHPUnit vous permet d'organiser vos tests en "suites" pouvant être exécutées séparément. Vous pouvez donc écrire une suite pour des tests de base de données personnalisés dans lesquels vous chargez un environnement WordPress (ou une partie de celui-ci), laissant ainsi le reste de vos tests sans WordPress .
Assurez-vous seulement d'écrire des classes qui résument autant d'opérations de base de données que toutes les autres classes de plug-in, afin que vous puissiez tester correctement la majorité des classes sans utiliser la base de données.
Pour la troisième fois, écrire du code facilement testable de manière isolée signifie écrire un meilleur code.
la source