Unit-Testing-Quellmodelle

10

Ich habe mehrere Modelle in meiner benutzerdefinierten Erweiterung, die nur dazu dienen, einige Auswahl- und / oder Mehrfachauswahlmöglichkeiten im Formular zum Hinzufügen / Bearbeiten meiner Entitäten auszufüllen.
Sie sind also das, was Magento "Quellmodelle" nennt.
Die beteiligten Werte sind immer gleich und die Methoden geben dasselbe zurück.
Wie soll ich diese testen? Oder noch besser, sollte ich Unit-Tests für sie schreiben?
Hier ist ein Beispiel.
Die folgende Klasse wird zum Hinzufügen / Bearbeiten von Formularen für ein Feld mit dem Namen typeund für die Rasterspalte desselben Felds verwendet.

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}
Marius
quelle

Antworten:

15

Dies ist ein großartiger Thread, und ich mag beide Antworten von @KAndy und @fschmengler sehr.
Ich möchte einige zusätzliche Gedanken hinzufügen, die ich wertvoll finde, wenn ich eine Frage stelle wie "Soll ich X testen?" oder "Wie soll ich X testen?".

Was könnte schiefgehen?

  • Ich könnte einen dummen Tippfehler machen (passiert die ganze Zeit).
    Dies rechtfertigt normalerweise nicht das Schreiben eines Tests.
  • Kopiere ich den benötigten Code aus dem Core oder einem anderen Modul und passe ihn dann an meine Bedürfnisse an?
    Ich finde das eigentlich eine sehr gefährliche Sache, die oft subtile Fehler hinterlässt. In diesem Fall schreibe ich lieber einen Test, wenn er nicht zu teuer ist. Wenn die Konfiguration der Quellmodelle tatsächlich basiert, werden sie IMO riskanter.
  • Kann es einen Konflikt mit einem anderen Modul geben?
    Dies gilt fast nur für Konfigurationscode. In einem solchen Fall möchte ich einen Integrationstest haben, der mir sagt, wann das passiert.
  • Könnte Magento die API in einer zukünftigen Version ändern?
    In diesem Fall sehr unwahrscheinlich, da Ihr Code nur von einer Schnittstelle abhängt. Aber je konkreter Klassen involviert sind oder wenn mein Code eine Kernklasse erweitert, wird dies zu einem potenziellen Risiko.
  • Eine neue PHP-Version könnte meinen Code beschädigen. Oder vielleicht möchte ich PHP 5.6 für die kommenden Jahre unterstützen.
    Auch hier sehr unwahrscheinlich, aber in einigen Fällen möchte ich einen Test, der mich warnt, sollte ich den Code in Zukunft ändern, um inkompatible Syntax zu verwenden.

Wie teuer ist es, den Code zu testen?

Dies hat zwei Aspekte:

  • Der Aufwand und die Zeit, die zum Schreiben eines Tests benötigt werden
  • Der Aufwand und die Zeit, die zum Testen des Codes benötigt werden, den ich manuell schreiben werde.

Während der Entwicklung eines Codeteils muss ich den Code, den ich schreibe, häufig ausführen, bis ich ihn für erledigt halte. Dies ist mit einem Unit-Test natürlich viel einfacher.

In Ihrem Fall ist das Schreiben eines Tests absolut billig. Es braucht nicht viel Zeit oder Mühe. @KAndy hat Recht, dass der gesamte Code beibehalten werden muss, aber andererseits müssen nicht alle Tests beibehalten werden.
Dies könnte ein Beispiel sein, in dem ich einen Komponententest schreibe, um zu überprüfen, ob ich keinen dummen Fehler mache, und ihn dann zu löschen, sobald die Klasse beendet ist. Wenn ein Test keinen langfristigen Wert liefert, halte ich das Löschen für sinnvoll.

Diese Frage ist auch wichtig für die Auswahl des zu schreibenden Testtyps: Einheit oder Integration zum Beispiel.

Wie wertvoll ist der Code, den ich schreibe?

Wenn ein Code, den ich schreibe, für den Service eines Moduls unerlässlich ist, teste ich ihn, unabhängig davon, wie trivial er ist.
Wenn es sich nur um eine kleine Dienstprogrammmethode handelt, die sich beispielsweise auf die Benutzeroberfläche konzentriert und nicht Teil der Geschäftslogik ist, ist dies möglicherweise nicht der Fall.

Muss sich der Code ändern?

Mit der Zeit habe ich mich so an die Testabdeckung gewöhnt, dass sich das Ändern von nicht abgedecktem Code sehr unsicher anfühlt. Dazu gehören so einfache Dinge wie das Hinzufügen einer Option zu einem Quellmodell, aber auch Dinge wie das Verschieben einer Klasse in einen anderen Ordner / Namespace oder das Umbenennen einer Methode.
Es ist von unschätzbarem Wert, Tests für solche Änderungen durchzuführen.

Benötigt es Dokumentation?

Wie schwer ist es, den Code zu verwenden? In Ihrem Beispiel ist es trivial, aber in einigen komplexeren Fällen ist ein Test für Dokumentationszwecke für andere Entwickler (oder mich selbst in einigen Monaten) großartig.

Erforschung und Lernen

Wenn ich an einem Code arbeite und nicht sicher bin, wie ich ihn testen soll, finde ich es sehr wertvoll, einen Test zu schreiben. Der Prozess gibt mir fast immer ein tieferes Verständnis dafür, womit ich es zu tun habe.
Dies gilt insbesondere für Entwickler, die sich immer noch als Testlerner betrachten.
Dies ist auch ein Beispiel, bei dem es möglicherweise sinnvoll ist, den Test anschließend zu löschen, da der Hauptwert darin bestand, zu lernen.

Disziplin und Stress

Das Festhalten an der Rot-Grün-Refaktor-Schleife hilft mir, schnell zu gehen. Dies gilt insbesondere unter Druck. Selbst wenn ein Teil des Codes nicht wirklich testwürdig ist, kann ich TDD dennoch folgen, insbesondere wenn der Code trivial zu testen ist.
Dies hält mich im Fluss und wachsam.

Was und wie testen?

Denken Sie auch daran, dass Sie den Test in sehr unterschiedlicher Granularität schreiben können.

  • Testen des genauen Rückgabewerts.
    Dies wird ein sehr strenger Test sein, der an jede Änderung angepasst werden muss. Möchten Sie, dass der Test beispielsweise unterbrochen wird, wenn sich die Reihenfolge der Elemente im Rückgabearray ändert?
  • Testen der Struktur des Rückgabewerts.
    Für das Quellmodell könnte dies sein, dass jedes Unterarray als zwei Datensätze überprüft wird, einer mit einem labelund einer mit einem valueSchlüssel.
  • Überprüfen der Klassenimplemente ArrayInterface.
  • Das Testen der Klasse bietet getOptions(), obwohl diese Methode nicht Teil der implementierten Schnittstelle ist.

Berücksichtigen Sie für jede mögliche Sache, die getestet werden kann, Wert, Wartbarkeit und Kosten.

Zusammenfassung

Um es zusammenzufassen: Es gibt keine echte Einzelantwort auf eine Frage, ob ein Code getestet werden soll oder nicht. Die Antwort ist je nach den Umständen für jeden Entwickler unterschiedlich.

Vinai
quelle
2
Gute Antwort! Ich möchte den Teil "Tests löschen, wenn sie keinen Wert mehr liefern" hervorheben - manchmal stören alte Tests, die während der ersten Entwicklung geholfen haben, nur langfristig.
Fabian Schmengler
1
Sie haben gerade 2 andere Fragen beantwortet, an denen ich Zweifel hatte. danke
Marius
6

Meiner Meinung nach gibt es keine allgemeine Antwort auf "Unit-Tests für Quellmodelle schreiben, ja oder nein".

Ich habe Unit-Tests für Quellmodelle geschrieben, aber das waren dynamische Quellmodelle, die externe Daten abriefen, und dort ist es absolut sinnvoll.

Für Quellmodelle, die nichts anderes als verherrlichte Arrays sind (wie in Ihrem Beispiel), würde ich keine Unit-Tests schreiben. Aber in gewisser Weise müssen Sie sicherstellen, dass Sie keinen Fehler gemacht haben. Es gibt mehrere Möglichkeiten:

  1. Gerätetest
  2. Integrationstest
  3. Schauen Sie sich die Konfiguration manuell an

Folgen Sie TDD? Wählen Sie dann zwischen (1) und (2) oder beiden. Andernfalls wählen Sie zwischen (2) und (3).


Wenn Sie die Quellmodelle für Systemkonfigurationsoptionen verwenden, kann ein Integrationstest (ein Test für alle Konfigurationsoptionen) den Konfigurationsabschnitt rendern und prüfen, ob die <select>Elemente vorhanden sind und die erwarteten Werte enthalten. Denken Sie daran, dass es nicht darum geht, eine bestimmte Klasse zu testen, sondern zu testen, ob alles richtig miteinander verbunden ist.


Und wie @KAndy sagte, würden Sie im Idealfall nicht so viel Boilerplate benötigen, sondern nur eine Basisklasse erweitern, die bereits Unit-getestet ist, und eine Eigenschaft überschreiben oder die Werte in der externen Konfiguration definieren.

In diesem Szenario bieten Komponententests für konkrete Implementierungen keinen zusätzlichen Wert. Wenn Sie viele dieser Klassen haben, ist es möglicherweise eine gute Idee, selbst eine Basisklasse zu schreiben ArraySource, sofern Magento diese nicht bereitstellt.


Mit einer solchen Basisklasse könnte Ihr Quellmodell folgendermaßen aussehen:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}
Fabian Schmengler
quelle
Danke für die Bestätigung. Ich werde versuchen, meine verherrlichten Arrays in eine konfigurierbare Optionsliste umzuwandeln. Gilt dies jedoch auch für Modelle, die genau N Optionen haben werden? Wie ein "Status" -Quellmodell.
Marius
Ich sehe nicht, wie sich ein "Status" -Quellmodell von Ihrem "Autorentyp"
-Beispiel
weil ich die Werte 0 und 1 (als Klassenkonstanten) im Frontend verwenden könnte, um zum Beispiel die aktivierten Entitäten zu filtern. Ich meine, in diesem Fall haben die Optionswerte eine Logik hinter sich. Sie sind nicht nur key=>valuePaare.
Marius
Aber diese Logik ist nicht Teil des Quellmodells, oder? Das heißt, es spielt hier keine Rolle. Sie haben immer noch die Konstanten, die Sie an anderen Stellen verwenden können. Ich habe ein Beispiel hinzugefügt, wie ich eine solche Implementierung verwenden würde.
Fabian Schmengler
5

Wie soll ich diese testen?

Ich denke du solltest nicht.

Fügen Sie dem System Code hinzu, um die Support- und Wartungskosten zu erhöhen. Der Testprozess sollte jedoch LEAN sein .

Mehr über diesen Code sollte nicht existieren. Ich glaube, als in Magento sollte eine generische deklarative Methode sein, um Sätze von Optionen zu definieren, die an verschiedenen Orten verwendet werden sollen. Und Ihre Zurückhaltung, einen Test für diese Klasse zu schreiben, zeigt, dass ich nach schlechtem Code rieche

KAndy
quelle
1
Vielen Dank. Sie sagen also, dass diese Optionen nicht fest codiert sein sollten, sondern aus einer Konfigurationsdatei (z. B. di.xml) stammen sollten. Ich kann das machen. Aber gilt das auch für Statusquellenmodelle? Ich meine, wenn ich dieselbe Klasse wie oben habe, aber nur mit aktiviertem und deaktiviertem Status (1,0), sollte ich denselben Konfigurationsansatz verwenden? Wenn ja, möchte ich sagen, dass es für mich wie Überentwicklung aussieht.
Marius