Moderne Benutzeroberflächen, besonders MacOS und iOS, haben viele "zufällige" Animationen - Ansichten, die durch kurze animierte Sequenzen erscheinen, die größtenteils vom System orchestriert werden.
%Vor%Gelegentlich haben wir vielleicht eine etwas aufwendigere Animation, etwas mit einer Animationsgruppe und einem Abschlussblock.
Nun kann ich mir Fehlerberichte wie folgt vorstellen:
Hey - diese nette Animation, wenn myNewView erscheint, passiert nicht in der neuen Version!
Wir möchten also, dass Komponententests einige einfache Dinge tun:
Aber natürlich müssen alle diese Tests einfach zu schreiben sein und den Code nicht schlechter machen; Wir wollen die Einfachheit der impliziten Animationen nicht mit einer Menge testgetriebener Komplexität verderben!
Also, Was ist ein TDD-freundlicher Ansatz zur Implementierung von Tests für Casual-Animationen?
Nehmen wir ein konkretes Beispiel, um zu veranschaulichen, warum wir einen Komponententest wünschen. Nehmen wir an, wir haben eine Ansicht, die eine Reihe von WidgetViews enthält. Wenn der Benutzer ein neues Widget mit einem Doppelklick erstellt, sollte es zunächst winzig und transparent erscheinen und sich während der Animation auf die volle Größe ausdehnen.
Nun, es ist wahr, dass wir das Systemverhalten des Systems nicht testen wollen. Aber hier sind einige Dinge, die schief gehen könnten, weil wir Dinge verpfuschen:
Die Animation wird im falschen Thread aufgerufen und nicht gezeichnet. Aber im Verlauf der Animation rufen wir setNeedsDisplay auf, damit das Widget schließlich gezeichnet wird.
Wir recyceln ausgediente Widgets aus einem Pool verworfener WidgetControllers. NEUE WidgetViews sind zunächst transparent, einige Ansichten im Recycle-Pool sind jedoch immer noch undurchsichtig. Also die Fade passiert nicht.
Einige zusätzliche Animationen beginnen mit dem neuen Widget, bevor die Animation beendet wird. Als Ergebnis beginnt das Widget zu erscheinen und beginnt dann zu ruckeln und kurz zu blinken, bevor es sich beruhigt.
Sie haben die drawRect: -Methode des Widgets geändert und das neue drawRect ist langsam. Die alte Animation war in Ordnung, aber jetzt ist es ein Durcheinander.
Alle diese werden in Ihrem Support-Protokoll angezeigt als "Die Create-Widget-Animation funktioniert nicht mehr." Und ich habe die Erfahrung gemacht, dass es für den Entwickler sehr schwierig ist, sobald man sich an eine Animation gewöhnt hat, sofort festzustellen, dass eine nicht zusammenhängende Änderung die Animation zerstört hat. Das ist ein Rezept für Komponententests, oder?
Die Animation wird im falschen Thread aufgerufen und nicht gezeichnet. Aber im Verlauf der Animation nennen wir setNeedsDisplay, also Schließlich wird das Widget gezeichnet.
Führen Sie keinen direkten Test dafür durch. Verwenden Sie stattdessen Assertions und / oder erhöhen Sie Exceptions, wenn sich die Animation im falschen Thread befindet. Unit-Test, dass die Assertion eine Ausnahme angemessen auslöst. Apple macht dies aggressiv mit ihren Frameworks. Es hält dich davon ab, sich in den Fuß zu schießen. Und Sie werden sofort wissen, wenn Sie ein Objekt außerhalb gültiger Parameter verwenden.
Wir recyceln ausgediente Widgets aus einem Pool von verworfenen WidgetController. NEW WidgetViews sind zunächst transparent, aber einige Ansichten im Recycling-Pool sind immer noch undurchsichtig. Also das Überblenden nicht passieren.
Aus diesem Grund sehen Sie in UITableView Methoden wie dequeueReusableCellWithIdentifier
. Sie benötigen eine öffentliche Methode, um das wiederverwendete WidgetView zu erhalten, welches die perfekte Möglichkeit zum Testen von Eigenschaften wie Alpha ist, die entsprechend zurückgesetzt werden.
Einige zusätzliche Animationen beginnen mit dem neuen Widget vor dem Animation endet. Als Ergebnis wird das Widget angezeigt und dann beginnt zu ruckeln und blinkt kurz bevor es sich beruhigt.
Wie Nummer 1. Verwenden Sie Behauptungen, um Ihrem Code Regeln vorzugeben. Unit-Test, dass die Assertions ausgelöst werden können.
Sie haben die drawRect: -Methode des Widgets und die neue Methode geändert DrawRect ist langsam. Die alte Animation war in Ordnung, aber jetzt ist es ein Chaos.
Ein Komponententest kann nur das Timing einer Methode sein. Ich tue dies oft mit Berechnungen, um sicherzustellen, dass sie innerhalb einer angemessenen Frist bleiben.
%Vor%Ich kann Ihre Frage auf zwei Arten lesen, also möchte ich diese trennen.
Wenn Sie fragen: "Wie kann ich testen, ob das System tatsächlich die von mir angeforderte Animation ausführt?", würde ich sagen, dass es sich nicht lohnt. Meine Erfahrung sagt mir, es ist eine Menge Schmerz mit nicht viel Gewinn und in diesem Fall wäre der Test spröde. Ich habe festgestellt, dass es in den meisten Fällen, in denen wir Betriebssystem-APIs nennen, den größten Nutzen bietet, wenn man davon ausgeht, dass sie funktionieren und solange funktionieren, bis das Gegenteil bewiesen ist.
Wenn Sie fragen: "Wie kann ich testen, ob mein Code die richtige Animation anfordert?", dann ist das interessanter. Sie werden ein Framework für Testdoppler wie OCMock benötigen. Oder Sie können Kiwi verwenden, das ist mein favorisiertes Test-Framework und hat Stubbing und Mocking eingebaut.
Mit Kiwi können Sie beispielsweise Folgendes tun:
%Vor%Sie wollen nicht wirklich auf die Animation warten; das würde die Zeit dauern, die die Animation benötigt, um zu laufen. Wenn Sie ein paar tausend Tests haben, kann dies summieren.
Effektiver ist es, die statische UIView-Methode in einer Kategorie so abzuspielen, dass sie sofort wirksam wird. Fügen Sie diese Datei dann in Ihr Testziel (aber nicht in Ihr App-Ziel) ein, damit die Kategorie nur in Ihren Tests kompiliert wird. Wir benutzen:
%Vor%Der obige Befehl führt den Animationsblock einfach sofort aus, und der Completion-Block wird sofort ausgeführt, wenn er ebenfalls bereitgestellt wird.
Tags und Links ios animation design-patterns cocoa tdd