Autotests ohne Schmerzen

    BildHallo Habra!

    Ich werde Ihnen keinen anderen modischen Rahmen für Tests anbieten, sondern nur den Ansatz für Tests und Dokumentation zeigen, den ich in in i-Free entwickelten Projekten verwende. Es könnte Ihnen gefallen, und Sie werden Projekte auf die gleiche Weise organisieren oder mich auf offensichtliche Probleme hinweisen.

    Viele Webentwickler schreiben keine Tests, und ich bin keine Ausnahme. Tests reduzieren jedoch die Anzahl der Fehler. Wenn Ihre Anwendung immer mehr wird, können Sie den Tests nicht entkommen. Außerdem habe ich in kleinen Unternehmen häufig Junioren getroffen, die es im Allgemeinen vorziehen, Code in Texteditoren zu schreiben (dies erhöht die Anzahl der Fehler, da die Redakteure den Code nicht überprüfen). Wie fange ich langsam an, Tests ohne Schmerzen und Leiden durchzuführen?! Es gibt einen Ausweg - Autotests zu verbinden.

    Die Essenz des Beitrags im Bild links. Das hat mir bisher in meiner täglichen Arbeit gefehlt. Ich wollte ein Tool, mit dem Sie sehr einfach in den Code eintauchen und eine allgemeine Schlussfolgerung über dessen Überlebensfähigkeit und Eignung ziehen können.

    Automatische Tests


    Die Probleme:
    • Der Entwickler ist zu faul, um die Tests selbst zu schreiben
    • Der Entwickler ist zu faul, um zusätzlichen Code für Tests zu schreiben
    • Der Entwickler ist im Allgemeinen zu faul, um über Tests nachzudenken

    Lösung: Stellen Sie über die API eines vom Entwickler verwendeten Frameworks eine Verbindung zur Anwendung her und führen Sie über dieses Programm automatische Tests durch.

    Wo verbinden?
    • Event-Hooking-Funktionen (wie $ ("# button) .click (), core.addEvent (), helper.onClick () usw.)
    • Listener-Funktionen (wie $ ("# button) .on (), event.listen (), mediator.listen () usw.)
    • Initialisierungsfunktionen (wie $ (body) .ready (), utils.ready () usw.)
    • Rückruffunktionen für AJAX-Anfragen

    Was zu überprüfen ist:
    • Leistung mit ungültigen Argumenten
    • Das Vorhandensein von zufälligen Fehlern aufgrund von Tippfehlern (wie fehlende;)
    • Das Vorhandensein aller für das Skript erforderlichen DOM-Elemente

    Sie können alle Funktionen in einer Reihe in try / catch mit einer Reihe von zufälligen Parametern und ohne diese aufrufen. Beobachten Sie, welche der Funktionen abfallen. Überprüfen Sie außerdem alle Links und Abfragen der DOM-Elemente und überprüfen Sie deren Vorhandensein im Layout.

    Was uns dies gibt:
    Es ist klar, dass dies ein ziemlich bedeutungsloser Test im Hinblick auf die Logik einer Webanwendung ist. Andererseits können wir die Versammlung ohne allzu großen Aufwand und in Eile auf völlig dumme Tippfehler und Absurditäten überprüfen.

    Boni:
    • Es kann argumentiert werden, dass ein solcher Selbsttest 90% der Funktionen der Anwendung abdecken kann.
    • Ein solcher Test erfordert vom Entwickler keinerlei Aufwand in Bezug auf Support, Updates usw. Grob gesagt kann er überhaupt nicht an sie denken
    • Es wird immer noch Fehler geben, daher gibt es keine unnötige Überprüfung
    • Um einen Test durchzuführen, müssen wir die Struktur unserer Anwendung überhaupt nicht ändern


    Ich wiederhole noch einmal:
    • Dies hebt Unit-Tests nicht auf!
    • Dies ist in Bezug auf die Anwendungslogik sinnlos!
    • Tests für Tests - schlecht


    Aber! Es ist kostenlos und mühelos und besser als nichts. Wenn Sie dies Ihren Junioren zeigen, werden sie überzeugt sein, dass es wirklich einfach und schmerzfrei ist, was bedeutet, dass sie den Tests loyaler sind, was bedeutet, dass sie schnell zu dem Gedanken kommen, Unit-Tests zu schreiben.

    Arbeitsschema:
    Bild

    Wir haben unsere Anwendung bestehend aus einer Reihe von Modulen. Um seine Funktion zu überprüfen, müssen wir für jedes Modul einen Komponententest schreiben. Es ist gut, wenn wir dies tun, aber tatsächlich werden die meisten Entwickler keine Zeit damit verbringen, insbesondere in kleinen Anwendungen. Wenn Sie eine vorgefertigte Lösung zum Testen verwenden, bei der der Code der Module geändert werden muss, wird dies niemand tun. Andererseits ist das Einfügen einiger Funktionen in die Ebene zwischen Bibliotheken und der Anwendung sehr einfach (wenn Sie keine solche Ebene haben, können Sie diese Funktionen in die Bibliothek selbst einfügen). Und am Ausgang bekommen wir bereits mindestens eine Art von Test (Sie können es Test "von einem Narren" nennen, der alle Tasten in einer Reihe drückt). Nun, wenn Sie noch eine Schicht hatten - im Allgemeinen wunderbar. Sie können auch die AJAX-Anforderungsfunktion ersetzen.

    Wenn Ihr Code in Module unterteilt ist und wie folgt gestaltet ist:

    (function(global) {
    	var module = {
    		_methodA: function() {},
    		_methodB: function() {},
    		_methodC: function() {},
    		init: function() {}
    	}
    	module.init();
    })(this);
    

    oder ähnliche Konstruktionen - dann kann das gesamte Modul sofort in Experimente exportiert werden.

    Ich habe ein Testobjekt in mir und alles was möglich ist - ich fing an es zu schieben:

    test.add();           // ждет на входе объекты типа модулей
    test.addFunction();   // ждет на входе функции
    

    Und dann ist alles einfach. Innerhalb dieses Tests gibt es mehrere Arrays, in denen eine Reihe von Rückrufen und Modulen gesammelt werden. Die Ausgabe ist eine andere Methode:
    test.start();
    

    Zu diesem Zeitpunkt wird die Überprüfung aller in Arrays gesammelten Daten gestartet. Und das alles wird im try / catch-Konstrukt verifiziert. Wenn jemand stirbt, wird eine Benachrichtigung in der Konsole angezeigt und das nächste Opfer aus dem Array entfernt.

    Wenn wir Funktionen testen, werden sie zusätzlich zu einem einfachen Aufruf auch mit Parametern aufgerufen. Tatsächlich gibt es eine umfassende Suche von null bis vier Parametern. Jeder Parameter nimmt eine Reihe von Werten in der Reihenfolge an (-1, 0, 1, "Zeichenfolge", wahr, falsch, [], {} usw.).

    Bild

    Wenn wir ein Modul nehmen, ist dies ein Objekt. Wir durchlaufen die Eigenschaften des Objekts und wenn wir auf eine Funktion stoßen, überprüfen wir sie gemäß dem obigen Algorithmus. Weil Alle meine Module haben die gleiche Struktur, Sie können noch ein paar Punkte überprüfen. Zum Beispiel, um herauszufinden, ob alle DOM-Elemente gefunden / erstellt wurden. Links zu ihnen werden in der Eigenschaft _nodes gespeichert, über die fast alle Module verfügen. Zum Beispiel:

    var module = {
    	_nodes: {
    		table: DOM_element,
    		link: DOM_element
    	},
    	_methodA: function() {},
    	_methodB: function() {},
    	_methodC: function() {},
    	init: function() {}
    }
    

    Wenn wir beim Durchlaufen des Objekts module._nodes plötzlich null finden, bedeutet dies, dass etwas das Modul verhindert hat. Möglicherweise hat die Initialisierung nicht funktioniert oder einige Elemente sind in der DOM-Struktur der Seite verschwunden. Ich werde sofort reservieren, dass ich den Link zu einem Element nur einmal ziehe. Der Rest der Zeit wird im Modul gespeichert und statt:

    $("#name").html("Ваня");
    

    Ich werde haben:

    var node = this._nodes.name;
    if(node) node.innerHTML = "Ваня";
    

    Bild

    Stuby und Moki


    Um Sergey Teplyakov zu zitieren:
    Ehrlich gesagt bin ich kein großer Fan von Designänderungen, nur um die "Testbarkeit" des Codes zu gewährleisten. Wie die Praxis zeigt, ist ein normales OO-Design entweder bereits ausreichend „getestet“ oder erfordert nur minimale Körperbewegungen, um dies zu erreichen. Einige zusätzliche Gedanken zu diesem Thema finden Sie im Artikel „Ideale Architektur“.

    Das Original kann hier eingesehen werden http://habrahabr.ru/post/134836/

    Wenn Sie noch nie davon gehört haben, ist der Stub wie ein Server oder eine Art externes gefälschtes Objekt, das beim Zugriff unterschiedliche Ergebnisse erzielt. Sozusagen ein Stub-Objekt für Tests. Moki ist das Gleiche, sie berücksichtigen nur Statistiken, die sie gezogen haben und wie oft.

    Müssen Sie etwas auf dem Server für Tests ändern?
    - Nein, wir haben nur einen Stub anstelle der AJAX-Anfrage eingefügt.

    Müssen Sie anstelle der AJAX-Anforderung im Anwendungscode etwas ändern?
    - Nein, wir können die Anforderungsfunktion in der Bibliothek selbst ändern, ohne unseren Code zu berühren. Daher spielt es keine Rolle, wo und wie oft die Funktion aufgerufen wird - sie zieht immer den Stub.

    Zum Beispiel haben Sie den Code:

    $.ajax({
        url: "ajax/test.html",
        success: function(data) {
            alert(data.message);
        }
    });
    

    Anstatt es für Tests auseinanderzunehmen und einen Rückruf herauszuholen, ist es besser, Ajax zu analysieren. Wir gehen wieder von der Seite der Bibliotheks-API weg, ohne unseren Code zu berühren. Ja, natürlich ist es nicht einfach, jQuery oder eine andere Bibliothek zu analysieren und in unsere Probes zu "stecken", aber Sie können jederzeit eine eigene dünne Schicht zwischen der Bibliothek und dem Code schreiben. Auf diese Weise können Sie nicht nur Tests ohne Schmerzen durchführen und Stubs durch reale Objekte ersetzen, sondern mit einem zusätzlichen Bonus erhalten Sie auch die Möglichkeit, von einem Framework zum anderen zu wechseln.

    Es ist klar, dass solche Tests Hadcorn sind und nur einige offensichtliche Fehler zeigen können. Aber es ist besser als nichts. Darüber hinaus erfordern sie keinen Aufwand, um sie zu unterstützen und zu schreiben. Für die Show gestartet, stellte sicher, dass alles zu funktionieren schien, schreiben Sie weiter. Ein weiteres Plus solcher Autotests ist, dass Sie ihnen Junioren beibringen können. Wenn sie sich daran gewöhnt haben und keine Angst mehr vor Tests haben, schreiben Sie normale Komponententests.

    Automatische Dokumentation


    Es kann schwierig sein, sich dazu zu bringen, Dokumentation zu schreiben. Andere zum Schreiben von Dokumentation zu zwingen ist fast unmöglich. Aber Sie können klein anfangen, vernünftige Argumente angeben und Tools angeben, die langsam helfen. In meinen i-Free-Projekten habe ich eine weitere Lösung hinzugefügt. Wie oben erwähnt, haben alle Module am Ende test.add (); Nach der Initialisierung der Anwendung wird ein Array abgerufen, in dem Links zu allen Modulen gespeichert sind. Sie können dieses Array durchgehen und, da die Module Objekte sind, ihren Baum zusammenstellen. Zum Beispiel:

    module 
    	_methodA -> function 
    	_methodB -> function 
    	_methodC -> function 
    	init -> function 
    

    Außerdem erhalten Sie Informationen zu Statistiken: eine Liste externer Methoden, eine Liste interner Methoden usw. Im einfachsten Fall müssen wir nur diesen Baum signieren:

    module 
    	_methodA -> Запускает рендер.
    	_methodB -> Обрабатывает результаты и отдает массив.
    	_methodC -> Запрашивает результаты у сервера.
    	init -> Инициализирует модуль. 
    

    Weil Wir haben auch Zugriff auf die Liste aller zugewiesenen Ereignisse. Wir können sie filtern und Listen darüber abrufen, was das Modul hört und was veröffentlicht wird. Ein Beispiel dafür, wie es im Leben aussieht:

    Bild

    Anhand der empfangenen Protokolle können Sie kurz eine Beschreibung des Moduls schreiben:

    Bild

    Mit dieser Code-Analyse können Sie dem Junior die Momente zeigen, die er in der Dokumentation beschreiben und im Code erklären sollte. Ein kurzer Blick auf die Struktur des Moduls kann Ihnen und Kollegen zeigen, dass möglicherweise einige Methoden des Objekts ausgetauscht werden sollten, weil Ihre Reihenfolge ist für das Verständnis des Prozesses etwas nicht logisch.

    Jetzt auch beliebt: