Das Problem:
Ichmöchteeine Klassetesten,dieeine neue Instanzeiner My_Notice -Klasseerstellt,die außerhalb des Plugins definiertist (nennen wires das "Haupt-Plugin").
Mein Komponententest weißnichts über My_Notice ,daesin einer Bibliothekeines Drittanbieters definiertist (genauergesagtin einem anderen Plugin).
Daher habeich (soweitich weiß)folgende Möglichkeiten:
Stub die My_Notice -Klasse: schwer zupflegen
Fügen Sie diebenötigten Dateien aus der Bibliothekeines Drittanbieters hinzu: Diesfunktioniertmöglicherweise,aberichmachemeine Tests wenigerisoliert
Die Klasse dynamisch stubben: Ichbin mirnicht sicher,ob dies überhauptmöglichist,aberes wäre sehr ähnlich,eine Klasse zu verspotten,außer dass sie auch die Definition derselbenerstellen sollte,damit die Klasse,dieichteste,instanziiert werden kannes.
Die Antwort von @gmazzap weist darauf hin,dass wir dies vermeiden sollten Art von Abhängigkeiten,dem stimmeich voll undganz zu.
Und die Idee,Stub-Klassen zuerstellen,scheintmirnichtgut zu sein: Ich würde lieber den Code des "Haupt-Plugins"mit allen Konsequenzeneinfügen.
Ich sehejedochnicht,wieiches andersmachen kann.
Hieristein Beispielfür den Code,denichtestenmöchte:
Klasse My_Admin_Notices_Handler {
öffentliche Funktion __construct (My_Notices $ admin_notices) {
$this- > admin_notices=$ admin_notices;
}}
/**
* Dies wirdmit der Aktion "enabled_plugin" verknüpft
* *
* @param string $plugin
* @parambool $network_wide
*/
öffentliche Funktionenabled_plugin ($plugin,$network_wide) {
$this- > add_notice ('Plugin','aktiviert',$ Plugin);
}}
/**
* @param string $type
* @param string $ action
* @param string $plugin
*/
private Funktion add_notice ($type,$ action,$plugin) {
$message='';
if ('aktiviert'===$ action) {
if ('plugin'===$type) {
$message=__ ('% 1s Eine Nachrichtfür Plugin (s)','my-test-domain');
}}
if ('theme'===$type) {
$message=__ ('% 1s Eine Nachrichtfür das Thema','my-test-domain');
}}
}}
if ('aktualisiert'===$ action & amp; & amp; ('plugin'===$type|| 'theme'===$type)) {
$message=__ ('% 1s Eine weitere Nachrichtfür aktualisierte Themen oder Plugins','my-test-domain');
}}
if ($message) {
$ Notice=new My_Notice ($plugin,'wpml-st-string-scan');
$ Notice- > Text=$ Nachricht;
$ Notice- > Actions=Array (
neue My_Admin_Notice_Action (__ ('Jetzt scannen','Meine-Test-Domain'),'#'),
neue My_Admin_Notice_Action (__ ('Überspringen','Meine-Test-Domain'),'#',true),
);
$this- > admin_notices- > add_notice ($ Notice);
}}
}}
}}
Grundsätzlich verfügt diese Klasse übereine Methode,diemit enabled_plugin verknüpft wird. Diese Methodeerstellteine Instanzeiner "Benachrichtigungs" -Klasse,dieirgendwo von der an den Konstruktor übergebenen My_Notices -Instanzgespeichert wird.
Der Konstruktor der Klasse My_Notice empfängt zweigrundlegende Argumente (eine UID undeine "Gruppe") underhälteinige Eigenschaften (beachten Sie,dass dasgleiche Problemmit dem Code My_Admin_Notice_Action Klasse).
Wie kannich die Klasse My_Notice zueinerinjizierten Abhängigkeitmachen?
Natürlich könnteichein assoziatives Array verwenden,eine Aktion aufrufen,die vom "Haupt-Plugin" verknüpft wird und dieses Arrayin die Argumente der Klasse übersetzt,aberfürmich siehtesnicht sauber aus.
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.
Das Problemist wahrscheinlich die Art und Weise,wie Sie Ihren Code strukturiert haben,undnicht die Implementierung von Ansatz A oder B,abergenerische Fragen zum Testen von Einheiten wie diese werdenbei SO wahrscheinlichbessergestellt.Ich seheehrlichgesagt kein Problembeim Testen Ihres Codes,kann alsonichteinmal verstehen,mit welchem Problem Sie konfrontiert sind.Übrigens,nur weileine Antwort 30positive Stimmenerhalten hat,heißt dasnicht,dass dies dereinziggültige Wegist,Dinge zutun
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 Ich habeeinige Klarstellungen hinzugefügt,was das Problembeim Testen dieser Klasseist.Ich hoffe dasmachtmehr Sinn.Ich habe die Frage hier als Folge der ursprünglichen Frage hinzugefügt,die sichim selben Netzwerkbefindet.Da das Problem demin der ursprünglichen Fragegestellten sehr ähnlichist,bin ichmirnicht 100% sicher,obichesbei SO verschiebenmuss,aberjede Anleitungist sehr willkommen!
@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!
Das wareine andere Zeit,in der die Regeln hier anders waren,und die Antwortisteinfachnichtgut.Obwohljedes Wort dort wahrist undich damiteinverstandenbin,ist der Sinn des Schreibenseines Pluginsfür WordPress darin zuintegrieren. Wenn Sie alsoisolierttesten,erhalten Sienur sehr wenig,insbesondere wenn Ihr Code wie der hiergezeigte relativtrivialist.Dasisolierte Testenistgroßartig,aber das Testen sollte auchnützlich undnichtnur rein sein.
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.
Dies kann als Folge von Testing Hooks Callback angesehen werden.
Das Problem: Ichmöchteeine Klassetesten,dieeine neue Instanzeiner
My_Notice
-Klasseerstellt,die außerhalb des Plugins definiertist (nennen wires das "Haupt-Plugin").Mein Komponententest weißnichts über
My_Notice
,daesin einer Bibliothekeines Drittanbieters definiertist (genauergesagtin einem anderen Plugin). Daher habeich (soweitich weiß)folgende Möglichkeiten:My_Notice
-Klasse: schwer zupflegenDie Antwort von @gmazzap weist darauf hin,dass wir dies vermeiden sollten Art von Abhängigkeiten,dem stimmeich voll undganz zu. Und die Idee,Stub-Klassen zuerstellen,scheintmirnichtgut zu sein: Ich würde lieber den Code des "Haupt-Plugins"mit allen Konsequenzeneinfügen.
Ich sehejedochnicht,wieiches andersmachen kann.
Hieristein Beispielfür den Code,denichtestenmöchte:
Grundsätzlich verfügt diese Klasse übereine Methode,diemit
enabled_plugin
verknüpft wird. Diese Methodeerstellteine Instanzeiner "Benachrichtigungs" -Klasse,dieirgendwo von der an den Konstruktor übergebenenMy_Notices
-Instanzgespeichert wird.Der Konstruktor der Klasse
My_Notice
empfängt zweigrundlegende Argumente (eine UID undeine "Gruppe") underhälteinige Eigenschaften (beachten Sie,dass dasgleiche Problemmit dem CodeMy_Admin_Notice_Action Klasse).
Wie kannich die Klasse
My_Notice
zueinerinjizierten Abhängigkeitmachen?Natürlich könnteichein assoziatives Array verwenden,eine Aktion aufrufen,die vom "Haupt-Plugin" verknüpft wird und dieses Arrayin die Argumente der Klasse übersetzt,aberfürmich siehtesnicht sauber aus.