Comme je ne développe pas des tâches cron pour chaque plugin WordPress de mes clients, je peux passer parfois plusieurs heures à chercher pourquoi la tâche cron ne marche pas par oubli. Pour au final, comprendre que c´est la façon de tester qui n´est pas la bonne.
J´écris donc ce petit billet comme mémo pour tous ceux qui sont dans mon cas.
Pour cela j´utilise le plugin WP Control qui permet de voir toutes les tâches cron programées.
Vous trouverez la liste de ces tâches cron dans Outils > Événements Cron.
Si la vôtre n´est pas visible, il faut vérifier si le code passe par là par exemple via un wp_die() juste avant comme ça :
if ( ! wp_next_scheduled( 'h4a_cron_hook' ) ) {
wp_die( 'tu y vas un jour ?');
wp_schedule_event( time(), 'hourly', 'h4a_cron_hook' );
}
Une autre possibilité pour que la tâche cron ne soit pas visible est le fait que le deuxième paramètre string $recurrence pour la fréquence ne soit pas compatible. Voici donc la liste des possibilités de valeurs:
Il est possible d´ajouter d´autres valeurs grâce au filtre cron_shedules .
Là où c´est souvent confus est lors de la vérification du résultat. Déjà, le plugin WP Control vous permettra d´exécuter votre tâche cron sans attendre une heure ou un jour ou sans changer la valeur $recurrence à chaque fois.
Mais exécuter ou lancer la tâche ne veut pas dire que votre fonction se lancera. Pour éviter les erreurs il faut bien comprendre le mécanisme de la tâche cron.
Déjà cette tâche n´aboutira pas forcément chaque heure ou quotidiennement ou autres. Elle se lancera sous certaines conditions.
Le script PHP ne peut que se lancer seulement si quelqu´un ou une machine lance le chargement d´une page. Si il y a pas de chargement de page, la tâche cron ne peut aboutir.
De plus, cette tâche cron à l´heure prévue vient se mettre dans une pile pour attendre la prochaine fois que quelqu´un veut bien lancer une page. Sauf que sa place en tête de pile dure un temps limité (peut-être une minute, je dois encore pousser le sujet ). Donc, si personne vient charger une page du site Web où la fonction d´appel est censée se déclencher, il n´y aura aucun résultat et faudra attendre la prochaine fois.
Donc, une fois compris cela, pour tester si sa fonction d´appel de la tâche cron fonctionne, il faut avec WP Control :
Et là on est bon pour les tests !
Lorsque l´on veut vérifier si le lien est correcte dans le menu WordPress via Codeception, on se trouve face à un petit soucis : les sous-menus ne sont pas visibles sauf si on a cliqué sur le lien du menu (lien parent).
$I->click('All Posts');
ça ne marche pas, car le lien n´est pas visible.$I->click( 'All Posts' , \Codeception\Util\Locator::elementAt('//li[@id=menu-posts]/ul/li/a', 0 ) );
ça ne marche pas non plus, sans doute aussi pas le manque de visibilité.La solution à la fois simple et efficace et d´afficher via jquery le sous-menu pour cliquer ensuite dessus. Voici le test complet :
public function it_should_go_to_posts_page( AcceptanceTester $I ){ $I->loginAsAdmin(); $I->executeJS( 'jQuery("li#menu-posts > ul").css( { top: "0" } );' ); $I->click('All Posts'); $I->seeCurrentUrlEquals( "/wp-admin/edit.phps" ); }
Dans le projet wp-browser (version 06/06/2018), il est possible de tester directement via la configuration une installation et désinstallation d´un plugin. Par défaut, on retrouve un exemple dans muloader.suite.dist.yml
config: WPLoader: ... plugins: ['mu-plugin-1/plugin.php'] activatePlugins: ['mu-plugin-1/plugin.php']
Pour tester votre propre plugin, il suffit de :
config: WPLoader: ... plugins: ['mon-plugin/MonPlugin.php'] activatePlugins: ['mon-plugin/MonPlugin.php']
Où mon-plugin est le nom du dossier du plugin et MonPlugin.php est la classe d´entrée pour le plugin.
Avec ceci, au lancement de la commande codecept run acceptance
, avant les tests, Codeception viendra placer les fichiers du plugin dans le dossier wp-content/plugins du wordpress de test et à la fin des tests, il supprimera les fichiers du plugin.
Le problème, étant que la désinstallation ne consiste pas à cliquer sur le lien « supprimer » pour le plugin, mais elle ne fait que supprimer les fichiers. Ce qui peut-être gênant lorsque dans votre plugin, à la désinstallation, la fonction vient supprimer des tables dans la base de données par exemple. En effet, les tables resteront après le lancement des tests.
Pour que la suppression du plugin se fasse comme si un administrateur WordPress le ferait, il faut donc simuler le clique sur le lien « supprimer » dans la page des plugins.
Si vous mettez à jour wp-browser, vous devrez refaire ces étapes. C´est une solution provisoire.
Donc…
Pour cela, on va se baser sur la méthode « deactivatePlugin » se trouvant dans AcceptanceTesterActions.php pour créer une méthode « deletePlugin » que l´on ajoutera dans cette classe (AcceptanceTesterActions).
/** * [!] Method is generated. Documentation taken from corresponding module. * * In the plugin administration screen delete a plugin clicking the "Delete" link. * * The method will presume the browser is in the plugin screen already. * * @author Hive 4 Apps * * @param string|array $pluginSlug The plugin slug, like "hello-dolly" or a list of plugin slugs. * * @return void * @see \Codeception\Module\WPWebDriver::deletePlugin() */ public function deletePlugin($pluginSlug) { return $this->getScenario()->runStep(new \Codeception\Step\Action('deletePlugin', func_get_args())); }
Maintenant on voit, que cette méthode appelle une autre méthode « deletePlugin » qui cette fois-ci doit se trouver dans la classe WPBrowserMethods.php
/** * In the plugin administration screen delete a plugin clicking the "Delete" link. * * The method will presume the browser is in the plugin screen already. * * @author Hive 4 Apps * * @param string|array $pluginSlug The plugin slug, like "hello-dolly" or a list of plugin slugs. * * @return void */ public function deletePlugin($pluginSlug) { $plugins = (array) $pluginSlug; foreach ($plugins as $plugin) { $option = '//*[@data-slug="' . $plugin . '"]/th/input'; $this->scrollTo($option, 0, -40); $this->checkOption($option); } $this->scrollTo('select[name="action"]', 0, -40); $this->selectOption('action', 'delete-selected'); $this->click("#doaction"); }
Cette méthode va ajouter le comportement de l´administrateur.
Méthode il est temps de tester notre méthode dans le scénario de test.
class MonAcceptanceTestCest { public $plugin_dir_name = 'my-plugin'; public function it_should_activate_delete_correctly_plugin(AcceptanceTester $I ) { //Install/Activation $I->loginAsAdmin(); $I->amOnPluginsPage(); $I->activatePlugin($this->plugin_dir_name); $I->seePluginActivated($this->plugin_dir_name); $I->dontSeeElement('.xdebug-error'); //Deactivation $I->deactivatePlugin($this->plugin_dir_name); $I->seePluginDeactivated($this->plugin_dir_name); //Delete $I->deletePlugin( $this->plugin_dir_name ); $I->acceptPopup(); } }
Dans WordPress, pour supprimer un plugin, il faut valider une alerte javascript donc nous sommes obliger pour que tout fonctionne d´ajouter
$I->acceptPopup();
. Cette méthode ne fonctionne pas avec PhantomJS puisque qu´elle est compatible uniquement si il y a un affichage.
En effet, si vous lancez à ce niveau le test, vous obtiendrez sans doute une erreur.
Je vous conseille donc de commenter dans WPFilesystem.php la fonction _after() qui gère la suppression des fichiers du plugin automatiquement. Comme ils sont déjà supprimés, c´est inutile.
Et normalement, tout devrait fonctionner !
Pour tester un plugin WordPress, j´ai décidé d´utiliser le projet Codeception pour WordPress. Le projet Github à utiliser se nomme wp-browser.
J´ai utilisé la version datant du 6 juin 2018 ( pas trouvé de numéro de version ).
Une fois l´installation faite, ne connaissant pas le fonctionnement des outils tels que Selenium ou PhantomJS, je pensais que les tests tournaient sur PhantomJS en ayant fait les étapes suivantes :
PHANTOMJS="vendor/bin/phantomjs"
Je pensais que le framework utilisé la configuration du fichier : phantomjs.suite.dist.yml
En réalité, lorsque je lançais mes tests d´acceptance, Codeception utilisait le fichier : acceptance.suite.dist.yml où il est écrit ceci :
config : WPBrowser: url: '%WP_URL%' adminUsername: 'admin' adminPassword: 'admin' adminPassword: 'admin' adminPath: '/wp-admin'
Les tests étaient lancés donc via WPBrowser qui étend PHPBrowser. Il n´y avait donc pas de possibilité de tester de javascript par exemple. Il faut donc remplacer dans acceptance.suite.dist.yml WPBrowser par un WPWebDriver.
Codeception propose plusieurs solutions pour ajouter un Webdriver : voir ici
Mais il me semble nécessaire de donner plus de détails si on est comme moi un débutant en la matière.
En faite les étapes (en détail) sont les suivantes :
phantomjs --webdriver=4444
via un terminal après s´être placé dans le dossier vendor/bin où se trouve phantomjs.exe. Pour info, avec PhpStorm comme IDE, il suffit de cliquer droit sur phantomjs.exe et cliquer sur « run cmd shell » puis lancer la commande. Le serveur est ainsi lancé ! La preuve étant l´affichage de cette ligne :
[INFO - 2018-07-06T17:41:57.373Z] GhostDriver - Main - running on port 4444
PHANTOMJS="bin/vendor/phantomjs"
Vous pouvez désormais lancer vos tests d´acceptance.
Dans ce cas c´est un peu plus compliqué. Sélénium nécessite plus d´outils.
Il va vous falloir :
Remarque : si vous utilisez le terminal de Phpstorm et que vous venez d´installer JAVA, il va falloir redémarrer jetBrains Toolbox pour que la commande « java » puisse être reconnu.
Puis :
java -jar -Dwebdriver.gecko.driver=geckodriver.exe selenium-server-standalone-3.13.0.jar
si vous voulez utiliser firefox. ou java -jar -Dwebdriver.chrome.driver=chromedriver.exe selenium-server-standalone-3.13.0.jar
pour chrome.Il se peut que vous ayez pas mal de problèmes à ce stade-ci dû en partie à cause des incompatibilités entre les versions de Sélénium et les pilotes geckodriver et chromedriver. Pour ma part, la version sélénium 3.13 est incompatible avec la version 0.21.0 de geckodriver. Impossible dans les tests de sélectionner des éléments dans la page. Avec chromedriver, j´ai réussi après plusieurs heures en associant la version 2.40 avec sélénium 3.13.
Au lancement de vos tests, si vous optenez cette erreur :
[ConnectionException] Can't connect to Webdriver at http://127.0.0.1:4444/wd/hub. Please make sure that Selenium Server or PhantomJS is running.
C´est que vous devez lancer un serveur.
Lorsqu´au lancement de vos tests, vous rencontrez cette erreur :
[Facebook\WebDriver\Exception\UnknownServerException]
Error communicating with the remote browser. It may have died.
L´une des raisons peut-être une mauvaise version de chromedriver.exe, mais aussi, une mauvaise configuration. Dans acceptance.suite.dist.yml WPBrowser,
window_size: false # au lieu de '1024x768' qui n´est pas supporté.
Au lancement du serveur Sélénium, si vous rencontrez cette erreur :
Exception in thread "main" com.beust.jcommander.ParameterException: Unknown option: -Dwebdriver.chrome.driver=chromedriver.exe
Il est fort possible que c´est parce que le paramètre du webdriver dans la ligne de commande doit être avant selenium-server-standalone-3.13.0.jar
, pas après.
Lorsque l´on utilise le chromedriver.exe avec selenium 3.13 et que l´on obtient au lancement des tests ceci :
[Facebook\WebDriver\Exception\UnknownServerException]
java.net.ConnectException: Connection refused: connect
De mon côté, c´était un problème de version du pilote chromedriver. La version 2.40 est compatible avec le selenium 3.13.
Suite au développement d´un plugin assez copieux associé à plusieurs addons, j´ai fini par perdre mon temps en corrigeant à maintes reprises les mêmes bouts de code.
En effet, une fois avoir terminer une nouvelle fonctionnalité je sauvegardai sur notre projet Github et après plusieurs « commit », je découvrais que j´avais cassé pour la énième fois certaines fonctionnalités.
Il était temps de passer un cran dans la qualité et l´efficacité du travail. Vu l´ampleur des tests à faire avant chaque « commit », il n´y avait qu´une seule solution : les tests automatisés.
Cet article est donc né de mes recherches répondant à la question suivante :
– Quelle est la meilleure méthode pour tester correctement mon plugin avant de sauvegarder et passer à la prochaine version ?
N´ayant pas tout testé, impossible pour moi, comme pour beaucoup de développeur de savoir si il y a vraiment une meilleur méthode. Comme souvent, c´est une question de contexte et de feeling.
Mais au moins mes recherches ont abouti à une liste de 3 méthodes possibles pour tester son plugin WordPress :
La première solution consiste de faire ses tests avec PHPUnit pour la partie PHP et Qunit pour la partie javascript. Il existe un projet Github qui intègre un wordpress de test avec ces outils intitulé wordpress-develop.
C´est la méthode choisi ici.
Wordhat est un autre outil pour faire ses tests utilsant Behat mais avec un ensemble de fonctionnalités pour WordPress.
Une autre alternative à Behat est Codeception. Il existe une version orientée wordpress : wp-browser
Je ne sais pas si ça peut aider car l´article étant sur le site « codeception », il est partisan mais ça a le mérite d´être argumenté. Il s´agit d´un article mettant en comparaison Behat et Codeception : Voici l´article.
A vous de voir.