Leproblème:
Je veuxtester une classe qui crée unenouvelleinstance d'une classe My_Notice définieen dehors duplugin (appelons-la le "Pluginprincipal").
Montest unitairene sait rien de My_Notice carilest défini dans unebibliothèquetierce (un autreplugin,pour êtreprécis).
Par conséquent,j'ai ces options (pour autant queje sache):
Stub la classe My_Notice : difficile àmaintenir
Incluez lesfichiersnécessaires de labibliothèquetierce: celapeutfonctionner,maisje rendsmestestsmoinsisolés
Stub dynamiquement la classe:je ne saispas si c'estmêmepossible,mais ce seraittrès similaire à semoquer d'une classe,sauf que cela devrait également créer la définition de lamême,donc la classe queje testepourrainstancier ça.
La réponse de @gmazzap indique quenous devrions éviter de créer cela sorte de dépendances,ce queje suistout àfait d'accord.
Et l'idée de créer des classes stubne me semblepasbonne:je préfèreinclure le code du "Main Plugin",avectoutes les conséquences).
Cependant,je ne voispas commentpuis-jefaire autrement.
Fondamentalement,cette classe a uneméthode qui sera accrochée à enabled_plugin . Cetteméthode construit uneinstance d'une classe "notification",qui sera stockée quelquepartpar l'instance My_Notices passée au constructeur.
Le constructeur de la classe My_Notice reçoit deux arguments debase (un UIDet un "groupe")et obtient quelquespropriétés définies (sachez que lemêmeproblèmeest avec le code My_Admin_Notice_Action > classe).
Commentpuis-jefaire de la classe My_Notice une dépendanceinjectée?
Bien sûr,je pourrais utiliser untableau associatif,appeler une action,quiest accrochéepar le "Pluginprincipal"et quitraduit cetableau dans les arguments de la classe,mais celane meparaîtpas clair.
The issue:
I want to test a class which creates a new instance of a My_Notice class defined outside the plugin (let's call it the "Main Plugin").
My unit test knows nothing about My_Notice because it's defined in a third party library (another plugin, to be precise).
Therefore, I have these options (as far as I know):
Stub the My_Notice class: hard to maintain
Include the needed files from the third party library: this may work, but I'm making my tests less isolated
Dynamically stub the class: not sure if that's even possible, but it would be very similar to mocking a class, except that it should also create the definition of the same, so the class I'm testing will be able to instantiate it.
The answer from @gmazzap points out that we should avoid creating this sort of dependencies which is something I totally agree.
And the idea of creating stub classes doesn't seem good to me: I'd rather include the code of the "Main Plugin", with all the consequences).
However I don't see how can I do otherwise.
Here's an example of the code I'm trying to test:
class My_Admin_Notices_Handler {
public function __construct( My_Notices $admin_notices ) {
$this->admin_notices = $admin_notices;
}
/**
* This will be hooked to the `activated_plugin` action
*
* @param string $plugin
* @param bool $network_wide
*/
public function activated_plugin( $plugin, $network_wide ) {
$this->add_notice( 'plugin', 'activated', $plugin );
}
/**
* @param string $type
* @param string $action
* @param string $plugin
*/
private function add_notice( $type, $action, $plugin ) {
$message = '';
if ( 'activated' === $action ) {
if ( 'plugin' === $type ) {
$message = __( '%1s Some message for plugin(s)', 'my-test-domain' );
}
if ( 'theme' === $type ) {
$message = __( '%1s Some message for the theme', 'my-test-domain' );
}
}
if ( 'updated' === $action && ( 'plugin' === $type || 'theme' === $type ) ) {
$message = __( '%1s Another message for updated theme or plugin(s)', 'my-test-domain' );
}
if ( $message ) {
$notice = new My_Notice( $plugin, 'wpml-st-string-scan' );
$notice->text = $message;
$notice->actions = array(
new My_Admin_Notice_Action( __( 'Scan now', 'my-test-domain' ), '#' ),
new My_Admin_Notice_Action( __( 'Skip', 'my-test-domain' ), '#', true ),
);
$this->admin_notices->add_notice( $notice );
}
}
}
Basically, this class has a method which will be hooked to activated_plugin. This method builds an instance of a "notification" class, which will be stored somewhere by the My_Notices instance passed to the constructor.
The constructor of the My_Notice class receives two basic arguments (a UID and a "group") and gets some properties set (mind that the same issue is with the My_Admin_Notice_Action class).
How could I make the My_Notice class an injected dependency?
Of course, I could use an associative array, call some action, which is hooked by the "Main Plugin" and which translates that array in the class's arguments, but it doesn't look clean to me.
Leproblèmeestprobablement lafaçon dont vous avez structuré votre code,et non lamanière d'implémenter l'approche A ou B,mais des questions detest unitairegénériques comme celle-ci sontprobablementmieuxposées au SO.Jene voisfranchement aucunproblème avec letest de votre code,doncje nepeuxmêmepas comprendre quelest leproblème auquel vous êtes confronté.BTWjusteparce qu'une réponse a obtenu 30 votespositifsne signifiepas que c'est le seulmoyen valable defaire les choses
The problem is probably the way you structured your code, and not with how to implement approach A or B, but generic unit testing questions like this are probably better asked at SO. I frankly don't see any problem with testing your code so can't even understand what is the issue you are facing. BTW just because an answer got 30 upvotes doesn't mean that it is the only valid way to do things
@MarkKaplun J'ai ajouté quelquesprécisions sur leproblème dutest de cette classe.J'espère que cela aplus de sens.J'ai ajouté la questionici,comme un suivi de la question d'origine,qui setrouve dans lemême réseau.Étant donné que leproblèmeesttrès similaire à celuiposé dans la questioninitiale,je ne suispas sûr à 100% de devoir le déplacer chez SO,maistouteindicationest labienvenue!
@MarkKaplun I added some clarification about what is the issue with testing this class. I hope this makes more sense. I added the question here, as a follow up of the original one, which is in the same network. Since the issue is very similar to the one posed in the original question, I'm not 100% sure I must move it at SO, but any guidance is very welcome!
C'était une époque différente où les règlesici étaient différentes,et la réponsen'esttout simplementpasbonne.Bien que chaquemot soit vraiet queje suis d'accord avec lui,tout l'intérêt d'écrire unpluginpour wordpressest de s'intégrer avec lui,donctesterisolément vous donnetrèspeu,surtout si votre code comme celui que vousmontreziciest relativementtrivial.Lestestsisolés sontexcellents,mais lestests doivent également être utileset pas seulementpurs.
That was different time in which the rules here were different, and the answer is just not a good one. While every word there is true and I agree with it, the whole point of wrting a plugin for wordpress is integrating with it, so testing in isolation gives you very little especially if your code as in the one you show here is relatively trivial. Testing in isolation is great, but testing should also be useful and not just pure.
Celapourrait être considéré comme un corollaire du Test du rappel des hooks .
Leproblème: Je veuxtester une classe qui crée unenouvelleinstance d'une classe
My_Notice
définieen dehors duplugin (appelons-la le "Pluginprincipal").Montest unitairene sait rien de
My_Notice
carilest défini dans unebibliothèquetierce (un autreplugin,pour êtreprécis). Par conséquent,j'ai ces options (pour autant queje sache):My_Notice
: difficile àmaintenirLa réponse de @gmazzap indique quenous devrions éviter de créer cela sorte de dépendances,ce queje suistout àfait d'accord. Et l'idée de créer des classes stubne me semblepasbonne:je préfèreinclure le code du "Main Plugin",avectoutes les conséquences).
Cependant,je ne voispas commentpuis-jefaire autrement.
Voici unexemple de code quej'essaie detester:
Fondamentalement,cette classe a uneméthode qui sera accrochée à
enabled_plugin
. Cetteméthode construit uneinstance d'une classe "notification",qui sera stockée quelquepartpar l'instanceMy_Notices
passée au constructeur.Le constructeur de la classe
My_Notice
reçoit deux arguments debase (un UIDet un "groupe")et obtient quelquespropriétés définies (sachez que lemêmeproblèmeest avec le codeMy_Admin_Notice_Action > classe).
Commentpuis-jefaire de la classe
My_Notice
une dépendanceinjectée?Bien sûr,je pourrais utiliser untableau associatif,appeler une action,quiest accrochéepar le "Pluginprincipal"et quitraduit cetableau dans les arguments de la classe,mais celane meparaîtpas clair.