Das Gesamtbild der Unit-Tests



Dies ist keine Anleitung, welche Zeichen Sie in den Code-Editor eingeben müssen, um Unit-Tests zu erhalten. Dies ist Nahrung für den Geist, die konsumiert werden muss, bevor diese Maßnahmen ergriffen werden.

Das Thema Unit-Tests ist nicht so einfach, wie es scheint. Viele von uns Entwicklern kommen unter dem Druck von Kunden, Mitarbeitern, Kollegen, ihren Vorbildern usw. zu Unit-Tests. Wir verstehen schnell seinen Wert und nachdem wir die technischen Vorbereitungen abgeschlossen haben, vergessen wir das allgemeine Bild, wenn überhaupt, haben wir es jemals verstanden. In diesem Artikel werde ich kurz darauf eingehen, was Unit-Tests beides sind und was nicht, und in PHP werde ich gleichzeitig beschreiben, welchen Platz Unit-Tests im Bereich der Qualitätssicherung einnehmen.

Was ist Testen?


Bevor Sie sich mit Unit-Tests befassen, müssen Sie die Testtheorie selbst studieren, um keine Fehler zu machen, wie sie von den Autoren eines der beliebtesten PHP-Frameworks gemacht wurden: Sie zeigten Integrationstests auf ihrer Website und nannten sie Unit-Tests. Nein, Laravel, das sind keine Unit-Tests. Obwohl mich das nicht davon abhält, diesen Rahmen immer noch zu lieben.

Softwaretests sind definiert als "eine Untersuchung, die durchgeführt wird, um interessierten Parteien Informationen über die Produktqualität zu liefern". Dies steht im Gegensatz zu "Softwaretests sind eine Verschwendung des Projektbudgets durch Entwickler, die nichts Wichtiges tun und dann mehr Zeit und Geld verlangen, weil" nichts "sehr teuer sein kann." Hier gibt es nichts Neues.

Hier ist meine kurze Geschichte, ein Test zu werden:

  • 1822 - Differenzmaschine (Charles Babbage).
  • 1843 - Analytische Maschine (Ada Lovelace).
  • 1878 - Edison führt den Begriff "Bug" ein.
  • 1957 - Testen und Debuggen von Programmen (Charles Baker).
  • 1958 - Das erste Software-Testteam (Gerald Weinberg).
  • 1968 - Crisis PO (Friedrich Bauer).
  • 1970er Jahre - Wasserfallmodell, relationales Modell, Zerlegung, kritische Analyse ( Walkthrough ), Entwurf und Inspektion von Code, Qualität und Metriken, Entwurfsmuster.
  • 1980er Jahre - CRUD-Analyse, Systemarchitektur, Autotest, V-Modell, Zuverlässigkeit, Qualitätskosten, Verwendungsmethoden, OOP-Entwurfsmuster.
  • 1990er Jahre - Scrum, Usability-Tests, MoSCoW, heuristische Tests, Software-Automatisierung und -Tests.

Wenn Sie sich auf eine Generation von Millennials wie mich beziehen, werden Sie vielleicht erstaunt sein, dass die Testteams lange vor Ihrer Geburt existierten. Halten Sie für einen Moment inne, atmen Sie ein, aus, beruhigen Sie sich.
Die Geschichte zeigt, wie sich die Art der Tests, die für Interessenten als „gut genug“ angesehen wurden, im Laufe der Zeit geändert hat. Ungefähre Phasen, die während des Tests geführt wurden:

  • ... - Debugging 1956
  • 1957 - 1978 Demonstration
  • 1979 - 1982 Zerstörung
  • Schätzung von 1983 - 1987
  • 1988 - ... Prävention

Daher sind Unit-Tests erforderlich, um Diskrepanzen zwischen dem Projekt und der Implementierung zu vermeiden .

Was ist eigentlich Testen?


Es gibt verschiedene Klassifikationen von Softwaretests. Um den Ort des Unit-Tests besser zu verstehen, werde ich nur die am weitesten verbreiteten Ansätze erwähnen.

Tests sind: statisch und dynamisch, "Box" (weiße Box, schwarze Box, graue Box), Ebenen und Typen. Jeder Ansatz verwendet unterschiedliche Klassifizierungskriterien.

Statische und dynamische Prüfung


Statische Tests werden ohne Codeausführung durchgeführt. Dies umfasst Korrekturlesen, Verifizieren, Code-Revision (wenn Sie die Arbeit einer anderen / Paar-Programmierung beobachten), kritische Analyse, Inspektionen und so weiter.

Dynamische Tests, um die richtigen Ergebnisse zu erzielen, erfordern die Ausführung von Code. Zum Beispiel für Unit-Tests , Integration, System-, Akzeptanz- und andere Tests. Das heißt, das Testen wird unter Verwendung dynamischer Daten, Eingabe und Ausgabe durchgeführt.

Box-Ansatz


Nach diesem Ansatz sind alle Softwaretests in drei Arten von Boxen unterteilt:

  • White-Box- Tests überprüfen interne Strukturen und Module und ignorieren die erwarteten Funktionen für Endbenutzer. Dies können API-Tests, Fehlerinjektion, Komponententests und Integrationstests sein.
  • Black-Box- Tests interessieren sich mehr für die Funktionsweise der Software und nicht für die Funktionsweise . Dies bedeutet, dass Tester weder das Testobjekt noch dessen Funktionsweise unter der Haube verstehen müssen. Diese Art des Testens richtet sich an Endbenutzer, deren Erfahrung mit einer sichtbaren Oberfläche interagiert. Black Boxes umfassen modellbasierte Tests, Verwendungstests, Statusübergangstabellen, Spezifikationstests usw.
  • Das Testen des Typs „ graue Box “ basiert auf Kenntnissen über Softwarealgorithmen und Datenstrukturen (weiße Box), wird jedoch auf Benutzerebene durchgeführt (schwarze Box). Dies umfasst Regressionstests und Mustertests.

Um Sie zu verwirren, möchte ich sagen, dass Unit-Tests auch für die „Black Box“ gelten können, da Sie das zu testende Modul verstehen können, aber nicht das gesamte System. Obwohl es für mich immer noch eine "weiße Kiste" ist, und ich schlage vor, Sie stimmen dem zu.

Testlevel


Ihre Anzahl variiert, normalerweise im Bereich von 4 bis 6, und sie sind alle nützlich. Die Namen können auch unterschiedlich sein. Abhängig von der Unternehmenskultur können Sie Integrationstests als funktional, Systemtests als automatisiert usw. bezeichnen. Der Einfachheit halber werde ich 5 Ebenen beschreiben:

  1. Unit Testing
  2. Integrationstests.
  3. Testen von Komponentenschnittstellen.
  4. Systemtests.
  5. Prüfung der Betriebsabnahme.

Unit- Tests testen die Funktionalität eines bestimmten Codeteils, normalerweise jeweils eine Funktion. Beim Integrationstest werden die Schnittstellen zwischen den Komponenten überprüft, sodass die zusammengesetzten Module ein System bilden, das wie vorgesehen funktioniert. Dies ist ein wichtiger Punkt, da eine große Anzahl von Tests, die als Komponententests bezeichnet werden, tatsächlich Integrationstests sind und Entwickler sie als Module betrachten. Wenn Sie mehrere Module verwenden möchten, wird die Integration zwischen diesen Modulen getestet, nicht die Module selbst. Das Testen von Komponentenschnittstellen überprüft die zwischen verschiedenen Modulen übertragenen Daten. Zum Beispiel haben wir Daten von Modul 1 erhalten - geprüft - an Modul 2 übertragen - geprüft. Systemtests sind End-to-End-Tests, um die Einhaltung aller Anforderungen zu überprüfen. Betriebsabnahmetests werden durchgeführt, um die Betriebsbereitschaft zu überprüfen. Es ist nicht funktionsfähig, nur die Wartungsfreundlichkeit der Dienste wird überprüft, ob Subsysteme die Umgebung und andere Dienste beschädigen.

Arten von Tests


Jede Art von Test kann unabhängig von ihrer Stufe auch in andere Arten unterteilt werden. Es gibt mehr als 20 gängige Typen. Am häufigsten:

  • Regressionstests .
  • Abnahmetests.
  • Rauchprüfung
  • Uat
  • Zerstörende Prüfung .
  • Leistungstests.
  • Kontinuierliche Prüfung .
  • Usability-Tests.
  • Sicherheitstests.

Aus dem Namen geht hervor, warum diese oder jene Art von Tests beabsichtigt ist. Fett sind die Unit-Tests in PHP. Wenn Sie wirklich wollen, können Sie jeden dieser Begriffe auf Unit-Tests anwenden. Die Hauptvielfalt der Komponententests sind jedoch Regressionstests, bei denen überprüft wird, ob alle Module des Systems nach Änderungen am Code korrekt ausgeführt werden.

Jetzt wissen Sie, dass Komponententests dynamisch sind, zur White-Box-Klasse gehören, auf Modulebene durchgeführt werden, Regressionstests sind, aber modulare Tests können als viele Arten von Tests verstanden werden. Was sind Unit-Tests wirklich?

Was ist Unit Testing?


Ein V-Modell ist eine grafische Darstellung der oben genannten Ebenen, Typen und ihres Zwecks im Lebenszyklus der Softwareentwicklung.



Nach Überprüfung und Genehmigung der detaillierten Anforderungen für das Produkt, wenn mit dem Schreiben von Code begonnen wurde, werden Unit-Tests zur ersten Verteidigungslinie gegen Inkonsistenzen. Daher zwingen Unternehmen, die verstehen, was sie tun, Entwickler, Unit-Tests oder sogar TDD zu verwenden, da es viel billiger ist, Fehler in der Anfangsphase zu beheben als in den späteren.

Und das ist fair. Unit-Tests haben viele Vorteile. Sie sind:

  • Isolieren Sie jeden Teil des Programms und überprüfen Sie die Richtigkeit.
  • Helfen Sie, Probleme frühzeitig zu erkennen.
  • Sie lassen Entwickler in Bezug auf Eingabe, Ausgabe und fehlerhafte Bedingungen denken.
  • Sie geben dem Code ein bequemes Aussehen zum Testen und erleichtern das zukünftige Refactoring.
  • Vereinfachen Sie die Integration von Arbeitsmodulen (!).
  • Ersetzen Sie die technische Dokumentation teilweise.
  • Erzwungen, die Schnittstelle von der Implementierung zu trennen.
  • Sie beweisen, dass der Modulcode wie erwartet funktioniert (zumindest mathematisch).
  • Kann als Low-Level-Regressionstestsuite verwendet werden.
  • Demonstrieren Sie Fortschritte bei der unvollständigen Systemintegration.
  • Reduzieren Sie die Kosten für die Behebung von Fehlern (mit TDD - noch mehr).
  • Mit ihnen können Sie die Architektur der Anwendung verbessern, indem Sie die Verantwortung der Module bestimmen.
  • Wenn Sie es testen können, können Sie es an Ihr System anschließen.
  • Unit Testing macht Spaß!

Es gibt jedoch bestimmte Einschränkungen, über die Sie nachgedacht haben, wahrscheinlich beim Lesen dieser Liste:

  • Unit-Tests erkennen keine Integrationsfehler.
  • Jeder Boolesche Ausdruck erfordert mindestens zwei Tests, und die Anzahl wächst schnell.
  • Unit-Tests sind genauso fehlerhaft wie der Code, den sie testen.
  • Das Verknüpfen von Tests mit bestimmten Frameworks oder Bibliotheken kann den Workflow einschränken.
  • Die meisten Tests werden nach Abschluss der Entwicklung geschrieben. Es ist traurig. Verwenden Sie TDD!
  • Möglicherweise funktioniert das System nach einer kleinen Umgestaltung wie zuvor, aber die Tests schlagen fehl.
  • Die Entwicklungskosten steigen.
  • Menschliches Versagen: Kommentieren gebrochener Tests.
  • Menschliches Versagen: Hinzufügen von Problemumgehungen zum Code speziell zum Bestehen von Komponententests.

Letzteres bringt mich am meisten um. (Fast) in jedem Projekt, direkt im Quellcode der Arbeitsanwendung, finde ich Zeilen wie "Wenn es sich um einen Komponententest handelt, laden Sie eine Ersatz-SQLite-Datenbank, andernfalls laden Sie eine andere Datenbank" oder "Wenn es sich um einen Komponententest handelt, senden Sie keine E-Mail, andernfalls." senden “und so weiter. Wenn Ihre Anwendung eine schlechte Architektur hat, tun Sie nicht so, als könnten Sie miese Software mit einem guten Testdurchlauf reparieren, da dies nicht besser wird.

Ich habe oft mit Kollegen und Kunden darüber gesprochen, was ein guter Unit-Test ist. Er:

  • Schnell.
  • Automatisiert.
  • Steuert alle Abhängigkeiten vollständig.
  • Zuverlässig: Es kann in beliebiger Reihenfolge gestartet werden, unabhängig von anderen Tests.
  • Es kann nur im Speicher ausgeführt werden (keine Interaktionen mit der Datenbank, Lese- / Schreibvorgänge im Dateisystem).
  • Gibt immer ein einzelnes Ergebnis zurück.
  • Praktisch zum Lesen und Begleiten.
  • Testet die SUT-Konfiguration nicht (System im Test).
  • Hat eine klar definierte EINZELAUFGABE.
  • Es ist gut benannt (und verständlich genug, um das Debuggen zu vermeiden, nur um herauszufinden, was fehlschlägt).

Für diejenigen, die nach dem Lesen von „automatisiert“ grinsten: Ich wollte nicht PHPUnit oder JUnit in CI-Pipelines integrieren. Der Punkt ist, dass wenn Sie den Code ändern, ihn speichern und nicht wissen, ob die Module ihre Tests bestehen, sie nicht automatisiert sind, sondern sollten. Die beste Option ist die Dateiverfolgung.

Was sollte einem Unit-Test unterzogen werden?


In normalen Systemen müssen Komponententests geschrieben werden für:

  • Module - unteilbare isolierte Teile des Systems, die eine Aufgabe ausführen (Funktion, Methode, Klasse).
  • Öffentliche Methoden.
  • Geschützte Methoden, aber nur in seltenen Fällen und wenn niemand sieht.
  • Bugs und ihre Korrekturen.

Die Definition eines Komponententests hängt vom Entwickler ab, der den Code geschrieben hat. In PHP handelt es sich fast immer um eine Klassenmethode oder -funktion, da es sich um eine unteilbare Software handelt, die für sich genommen Sinn macht . Mehrmals habe ich gesehen, wie Entwickler ein Array von Miniklassen mit einer Methode als einzelnes Modul verwendeten. Dies ist sinnvoll, wenn für eine minimale Funktionalität mehrere Objekte erforderlich sind.

So können Sie selbst bestimmen, was ein Modul für Sie ist. Oder Sie können die Methoden einzeln testen, um dem Kerl das Leben zu erleichtern, der dann mit dem Code arbeitet.

Wenn Sie keine Unit-Tests durchführen, schlage ich vor, dies nach dem nächsten großen Fehler zu tun. Überprüfen Sie, mit welcher Methode es verknüpft wird, schreiben Sie einen fehlgeschlagenen Test mit den richtigen Argumenten und Ergebnissen, beheben Sie den Fehler und führen Sie den Komponententest erneut aus. Wenn es bestanden wird, können Sie sicher sein, dass dieser Fehler zum letzten Mal behoben werden musste (unter Berücksichtigung Ihrer spezifischen Eingabeszenarien).

Dieser Ansatz erleichtert das Verständnis von Unit-Tests. Analysieren Sie jede Methode separat. Datenanbieter können Ihnen dabei helfen, die Ein- und Ausgabe für jedes Szenario zu bestimmen, das Ihnen in den Sinn kommt. Unabhängig davon, was passiert, wissen Sie, was Sie erwartet.

Was muss NICHT getestet werden


Es ist etwas schwieriger festzustellen, dass Sie nicht testen müssen. Ich habe versucht, eine Liste von Elementen zu erstellen, die keinen Unit-Tests unterzogen werden müssen:

  • Funktionalität außerhalb des Modulumfangs (!)
  • Integration von Modulen mit anderen Modulen (!)
  • Nicht isoliertes Verhalten (nicht blockierbare Abhängigkeiten, reale Datenbanken, Netzwerk)
  • Private, sichere Methoden.
  • Statische Methoden.
  • Externe Bibliotheken.
  • Ihr Rahmen.

Ich bin sicher, dass Unit-Tests auf keine der oben genannten Methoden angewendet werden sollten, außer auf statische Methoden. Ich möchte argumentieren, dass statisch im Wesentlichen Prozeduralität bedeutet und in vielen Fällen prozedural ist. Wenn die statische Methode eine andere statische Methode aufruft, kann diese Abhängigkeit nicht überschrieben werden. Dies bedeutet, dass Sie jetzt isoliert testen. Und dann ist dies kein Unit-Test mehr. Auf der anderen Seite ist dies der Teil des Codes, der für sich alleine leben kann, er hat einen Zweck und er muss getestet werden, um sicherzustellen, dass er nicht kaputt geht, egal welchen Teil dieses dummen Systems der getestete Teil des Codes aufruft. Daher glaube ich, dass Sie statische Methoden testen können, wenn Sie sicher sind, dass die Ausgabe Ihres Tests durch keinen anderen Test geändert werden kann und die Sprache oder das Framework es Ihnen ermöglicht, nativ zu testen.

Wie schreibe ich Unit-Tests?


  • Schreiben Sie einen Code, der für Unit-Tests geeignet ist, und testen Sie ihn dann.
  • Schreiben Sie einen Code, der für Unit-Tests geeignet ist, und testen Sie ihn dann.
  • Schreiben Sie einen Code, der für Unit-Tests geeignet ist, und testen Sie ihn dann.

Wenn "dann testen" nicht ausreicht, bietet laracasts.com sehr gute Videos zum Testen von PHP-Einheiten. Es gibt viele Websites, die sich derselben Aufgabe in anderen Sprachen widmen. Ich sehe keinen Grund zu erklären, wie ich Unit-Tests durchführe, da sich die Tools ziemlich schnell ändern und ich beim Lesen dieses Textes von PHPUnit zu Kahlan wechseln kann. Oder nicht. Wer weiß.

Die Beantwortung der ersten Frage (Schreiben von Code, der für Unit-Tests geeignet ist) ist jedoch viel einfacher, und es ist unwahrscheinlich, dass sich die Situation im Laufe der Zeit wesentlich ändert:

  • FEST
  • TROCKEN
  • Das Fehlen neuer Schlüsselwörter im Konstruktor.
  • Das Fehlen von Schleifen im Konstruktor (und Übergängen, falls angegeben).
  • Mangel an statischen Methoden, Parametern, Klassen.
  • Fehlende setup () -Methoden: Objekte müssen nach der Erstellung vollständig initialisiert werden.
  • Das Fehlen von Singleton (globaler Status) und anderen nicht testbaren Antimustern.
  • Der Mangel an allmächtigen Objekten (Gottobjekten).
  • Mangel an Klassen mit gemischter Funktionalität (gemischte Anliegenklassen).
  • Keine versteckten Abhängigkeiten.

Wenn Sie nun wissen, was die Komponententests sind und was nicht, was Sie benötigen und was Sie nicht testen müssen, welchen Platz die Komponententests im Lebenszyklus der Softwareentwicklung einnehmen, können Sie sie einfacher implementieren. Es bleibt ein Rahmen oder eine Bibliothek nach Ihren Wünschen zu finden. Nehmen Sie im Zweifelsfall das De-facto-Standard-Framework / die Standardsprache.

Fazit: Unit-Tests sind sowohl für Entwickler als auch für Unternehmen sehr wichtig. Sie müssen geschrieben werden. Es gibt bewährte Methoden, mit denen Sie Module problemlos mit Tests abdecken können, hauptsächlich indem Sie die Module selbst vorbereiten. Alle diese Techniken sind jedoch ohne Kenntnis der in diesem Artikel beschriebenen Testtheorie nicht sinnvoll. Sie müssen in der Lage sein, Komponententests von Tests anderer Typen zu unterscheiden. Und wenn Sie ein klares Verständnis in Ihrem Kopf haben, wird es für Sie viel einfacher, Tests zu schreiben.

Source: https://habr.com/ru/post/de412695/


All Articles