Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[DE] Pragmatisches Unit Testing (uncut)

[DE] Pragmatisches Unit Testing (uncut)

05/2015 Meet Magento DE - including 17 previously unpublished slides

Cbc8378de58e66705678686057cffac9?s=128

Fabian Schmengler

May 11, 2015
Tweet

Transcript

  1. 11.05.2015 Pragmatisches Unit Testing Meet Magento DE 2015 Fabian Schmengler

    – Pragmatisches Unit Testing 1
  2. 11.05.2015 Agenda Fabian Schmengler – Pragmatisches Unit Testing 2 •

    Grundlagen: Warum automatisierte Tests? • Tests für Magento: Überblick und Beispiele • Ausblick auf Magento 2
  3. 11.05.2015 Fabian Schmengler – Pragmatisches Unit Testing 3

  4. 11.05.2015 Über mich • Fabian Schmengler • Informatik-Studium an der

    RWTH Aachen • Web-Entwicklung seit 2005 • Schwerpunkt auf Magento seit 2011 • integer_net GmbH seit 2014 • Verheiratet, zwei Kinder Fabian Schmengler – Pragmatisches Unit Testing 4
  5. 11.05.2015 Warum Automatisiertes Testen? • „Beweis“, dass Code wie erwartet

    funktioniert • Unerwünschte Nebeneffekte rechtzeitig erkennen • Refaktorisieren ohne Angst Fabian Schmengler – Pragmatisches Unit Testing 5
  6. 11.05.2015 Test Granularität • Unit Test – White Box: Komponenten

    isoliert testen • Integration Test – White Box: Zusammenspiel von Komponenten testen • System Test – Black Box: Funktionaler Test des Gesamtsystems Fabian Schmengler – Pragmatisches Unit Testing 6
  7. 11.05.2015 Testgetriebene Entwicklung (TDD) 1. Schreibe einen fehlschlagenden Test 2.

    Schreibe die minimale Implementierung, die den Test erfolgreich macht 3. Wiederhole (1) und (2) bis Feature komplett 4. Refaktorisiere bis Code sauber Fabian Schmengler – Pragmatisches Unit Testing 7
  8. 11.05.2015 Welchen Wert haben automatisierte Tests? (L * W) /

    (M * K) (zusätzlich gefundene Bugs) / (nicht gefundene Bugs) • L = Lebenszeit (Anzahl Durchläufe bis Test veraltet) • W = Wahrscheinlichkeit, neue Bugs zu finden (Erwartungswert für Bugs pro Durchlauf) • M = Bugs/Zeit, die beim manuellen Testen gefunden werden können • K = Kosten (Zeit für die Test-Implementierung) Sind automatisierte Tests wirklich zu teuer? Mit dieser Formel lässt es sich prüfen! Fabian Schmengler – Pragmatisches Unit Testing 8
  9. 11.05.2015 Central Paradox Of Automated Testing An automated test's value

    is mostly unrelated to the specific purpose for which it was written. It's the accidental things that count: the untargeted bugs that it finds – Brian Marick Fabian Schmengler – Pragmatisches Unit Testing 9
  10. 11.05.2015 Test nach Feature-Änderung • Feature Code (Blöcke): Geschäftslogik •

    Support Code: Rest • Test muss geändert werden, bevor er Fehler finden kann Fabian Schmengler – Pragmatisches Unit Testing 10
  11. 11.05.2015 Test nach Änderung anderer Features • Isolierte Unit Tests

    finden den Fehler nicht • Tests für die geänderten Features finden den Fehler nicht Fabian Schmengler – Pragmatisches Unit Testing 11
  12. 11.05.2015 Wert von Unit Tests • Ohne TDD wenig Nutzen

    – Falscher Alarm bei Feature-Änderung – Geringe Wahrscheinlichkeit, Fehler durch Nebeneffekte zu finden • Ohne Integrationstests kontraproduktiv – Vorgegaukelte Sicherheit! Fabian Schmengler – Pragmatisches Unit Testing 12
  13. 11.05.2015 Wert von Integrationstests • Abdeckung von Support Code –

    Hohe Wahrscheinlichkeit, Fehler durch Nebeneffekte zu finden Fabian Schmengler – Pragmatisches Unit Testing 13
  14. 11.05.2015 Testdaten • Äquivalenzklassen bilden! – z.B.: Eingabe zu klein

    (<1), Eingabe OK (1-100), Eingabe zu groß (>100) • Grenzwerte testen! – 0, 1, 100, 101 • Ungültige Eingaben testen! – -1, 0.5,Robert'); DROP TABLE students; – Fabian Schmengler – Pragmatisches Unit Testing 14
  15. 11.05.2015 Testdaten • In TDD: die „minimale Implementierung“, um einen

    Test erfolgreich zu machen, zwingt uns zu möglichst vollständigen Testdaten Fabian Schmengler – Pragmatisches Unit Testing 15 function testFaculty() { assert('faculty(1)===1'); assert('faculty(2)===2'); assert('faculty(3)===6'); } function faculty($n) { return $n; }
  16. 11.05.2015 Fabian Schmengler – Pragmatisches Unit Testing 16 Tests in

    Magento
  17. 11.05.2015 Testbarkeit in Magento • Magento 1 wurde ohne Tests

    entwickelt • Testbarkeit war also nie Qualitätskriterium • Isoliertes Testen von Modulen ist schwer – Zu viele Abhängigkeiten • Systemtests (z.B. Selenium) sind langsam – Laufzeit bei komplett durchgetestetem Shop > 1h – Kein direktes Feedback – Keine testgetriebene Entwicklung möglich Fabian Schmengler – Pragmatisches Unit Testing 17
  18. 11.05.2015 Abstraction Instability Graph Fabian Schmengler – Pragmatisches Unit Testing

    18
  19. 11.05.2015 EcomDev_PHPUnit To The Rescue! • Hat Unit Testing in

    der Magento Welt salonfähig gemacht – Fixtures – Mocks für Models, Helpers – Controller Tests mit Session Simulation – Tests für Layouts und Konfigurationen • Vielseitig ABER naturgemäß komplex und fehleranfällig Fabian Schmengler – Pragmatisches Unit Testing 19
  20. 11.05.2015 Unit Testing? • Unit Test für z.B. Block Rewrites

    oder Observer? – Ist das Model vollständig geladen? – Was steht in der Session? – Was wird aus der Registry geholt? • Aufwändiges Mocking notwendig • Bugs entstehen am ehesten, weil die Parameter nicht wie erwartet sind. Der Unit Test in seiner heilen Mock- Welt bekommt das nicht mit. • Isoliertes Testen für die meisten Magento-Anpassungen sinnlos Fabian Schmengler – Pragmatisches Unit Testing 20
  21. 11.05.2015 Eine wichtige Erkenntnis... EcomDev_PHPUnit = Integration Test Framework Fabian

    Schmengler – Pragmatisches Unit Testing 21
  22. 11.05.2015 Integrationstests • Zusammenspiel von Modul mit Magento => Integrationstests

    • Controller Tests => Integrationstests • Anpassungen von Magento mit mehr Support Code als Feature Code => Integrationstests • Auswirkung von Konfigurationen => Integrationstests • Tests mit EcomDev_PHPUnit sind in den meisten Fällen keine Unit Tests! Fabian Schmengler – Pragmatisches Unit Testing 22
  23. 11.05.2015 Tools für Integrationstests • Für Extensions: – EcomDev_PHPUnit •

    Alternativ PHPUnit mit eigener Bootstrap- Datei – Testen gegen verschiedene Magento Versionen mit Travis CI und Aoe Mage Test Stand • Für Shops: – xtest Fabian Schmengler – Pragmatisches Unit Testing 23
  24. 11.05.2015 TDD mit Magento • Für komplexen Business-Code mit wenig

    Interaktion zu Magento • PRO Tipp: Logik komplett von Magento entkoppeln und nur mit PHPUnit testen – Schnelle Testdurchläufe – Saubererer Code – Portierung zwischen Magento 1 und Magento 2 möglich Fabian Schmengler – Pragmatisches Unit Testing 24
  25. 11.05.2015 Beispiel 1: Fleischpaket-Modul • Fleischpakete werden dynamisch gebündelt je

    nach Lagerbestand der Einzelstücke • Komplexe Business-Logik – Nicht völlig entkoppelt von Magento aber überschaubare Abhängigkeit • Hohes Risiko von Bugs und Nebeneffekten – angepasster Bündel-Produkttyp – Rewrites von essentiellen Magento Models Fabian Schmengler – Pragmatisches Unit Testing 25 Beispiel 1
  26. 11.05.2015 Testdaten-getriebene Entwicklung Fabian Schmengler – Pragmatisches Unit Testing 26

    Testdaten aus Spezifikation Beispiel 1
  27. 11.05.2015 Eingabedaten für Grenzfälle • Paket aus zwei identischen Stücken

    (200g + 200g) • Menge für die gerade noch eine exakte Kombination möglich ist • n+1 • Menge für die gerade noch eine Kombination mit Gewicht-Toleranz möglich ist • n+1 • Maximal anfragbare Menge (10000) Fabian Schmengler – Pragmatisches Unit Testing 27 Beispiel 1
  28. 11.05.2015 TDD? • Selektionslogik testgetrieben mit Unit Tests entwickelt •

    Testdaten und Testcode separiert • Später Integrationstests mit gleichen Testdaten • Bei Bugs und Change Requests, Testdaten erweitert Fabian Schmengler – Pragmatisches Unit Testing 28 Beispiel 1
  29. 11.05.2015 Test in verschiedenem Kontext • Testdaten und Erwartungen definiert

    für: – Sequentielle Bestellung einzelner Pakete – Bestellungen mehrerer Pakete auf einmal – Neuberechnung nach Lagerbestand-Änderung • … und benutzt für Integrationstests im Kontext: – In den Warenkorb legen – Zusätzliche Menge in den Warenkorb legen – Menge im Warenkorb aktualisieren – Gesamten Warenkorb aktualisieren Fabian Schmengler – Pragmatisches Unit Testing 29 Beispiel 1
  30. 11.05.2015 Lessons Learned • Bei komplexen Fixtures zuerst einen Test

    schreiben, der die Daten aus der Fixture prüft – z.B. bei Bündelprodukten mit EcomDev_PHPUnit • Tests für alle möglichen Grenzfälle sind bei instabilem Code unverzichtbar – Instabil sind z.B.: viele Rewrites, viele Features die voneinander abhängen Fabian Schmengler – Pragmatisches Unit Testing 30 Beispiel 1
  31. 11.05.2015 Beispiel 2: IntegerNet_Anonymizer • Anonymisiert kundenbezogene Daten mit Dummy-Daten

    aus Faker • Berücksichtigt Assoziationen order address == customer address => dummy order address == cummy customer address • Logik als eigenständige Bibliothek (in lib/) • Magento-Modul implementiert nur Interfaces zum lesen und schreiben von Daten und delegiert an die Bibliothek Fabian Schmengler – Pragmatisches Unit Testing 31 Beispiel 2
  32. 11.05.2015 Logik von Magento entkoppelt Fabian Schmengler – Pragmatisches Unit

    Testing 32 Beispiel 2
  33. 11.05.2015 Unit Tests für Bibliothek • Testgetriebene Entwicklung mit PHPUnit

    • Test der Geschäftslogik – z.B. gleiche Eingabe => gleiche Ausgabe wenn der selbe Kunde assoziiert ist, sonst nicht. • Hier können Testdaten effizient variiert werden um Sonderfälle abzudecken • Testlaufzeit 150 ms (keine Datenbankzugriffe, keine Magento-Instantiierung) Fabian Schmengler – Pragmatisches Unit Testing 33 Beispiel 2
  34. 11.05.2015 Integrationstests für Magento-Modul • EcomDev_PHPUnit praktisch, aber nicht notwendig

    • Bridge Test: Geben die Bridge Implementierungen die korrekten Attribut-Werte zurück und funktioniert das setzen neuer Werte? • Anonymizer Test: Werden alle Daten tatsächlich verändert? • Beschränkung auf wenige Durchläufe, keine ausführlichen Testdaten notwendig • Testlaufzeit: 4 s Fabian Schmengler – Pragmatisches Unit Testing 34 Beispiel 2
  35. 11.05.2015 Lessons Learned • Enthält ein Modul genug Geschäftslogik, dass

    sie sinnvoll ohne Magento modelliert werden kann (Feature Code > Support Code)? Dann lohnt sich das Entkoppeln! • Bridge Pattern zur Anbindung an Magento • TDD und Unit Tests für Library, Integrationstest für Magento-Modul • Unit Tests mit PHPUnit 4.5, Mutation Testing mit Humbug zur Verbesserung der Tests Fabian Schmengler – Pragmatisches Unit Testing 35 Beispiel 2
  36. 11.05.2015 Systemtests • Funktionale Tests – Automatisierung bei guten Integrationstests

    nicht zwingend notwendig – xtest verbindet beides (via PHPUnit_Selenium) • Smoke Tests – Durchlaufen der Basis-Shop-Funktionalität, um gravierende Fehler zu entdecken • Selenium IDE (Firefox Plugin) – Für kurzlebige Tests super, auch während der Entwicklung – Für Regressionstests zu unzuverlässig Fabian Schmengler – Pragmatisches Unit Testing 36
  37. 11.05.2015 Fabian Schmengler – Pragmatisches Unit Testing 37 Mythen, Folklore

    und Fehler
  38. 11.05.2015 Mythen • Mythos: Hohe Code Coverage => Gute Tests

    – Werkzeug, nicht Ziel! – Code Coverage kann ungetesteten Code identifizieren – Gute Tests mit 50% Abdeckung > Schlechte Tests mit 99% Abdeckung • Mythos: Unit Tests sind teuer – Nicht wenn sie sinnvoll eingesetzt werden! – Noch einmal für die Projektmanager: (L * W) / (M * K) Fabian Schmengler – Pragmatisches Unit Testing 38
  39. 11.05.2015 Folklore • Folklore: Konfigurations-Tests $this->assertModuleVersion('0.1.0'); – Zeitverschwendung – Ausnahme:

    Rewrites testen, um Konflikte zu erkennen • Folklore: Unit Tests mit unzähligen Mocks – Führt letztendlich zu unwartbaren und unzuverlässigen Tests • Folklore: YAML Fixtures – Zusätzliche Abstraktionsebene und Fehlerquelle – Im Zweifel Produkte etc. lieber per Code vorbereiten Fabian Schmengler – Pragmatisches Unit Testing 39
  40. 11.05.2015 Fehler 1: Umgang mit fehlerhaften Tests • Typischer Umgang

    mit degenerierter Test Suite: – Phase 1: Viele Stunden mit Reparieren von Tests verbringen – Phase 2: Bekannte False Positives ignorieren – Phase 3: Alle Failures ignorieren. Keine neuen Tests mehr schreiben, weil eh nicht mehr erkennbar ist, welche Tests zu Recht fehlschlagen und welche nicht. • Broken Windows Theory • Defekte Tests lieber deaktivieren, als sie mitzuschleifen! • Test-Strategie regelmäßig hinterfragen: Tun wir (noch) das Richtige? Haben die Tests noch Wert? Fabian Schmengler – Pragmatisches Unit Testing 40
  41. 11.05.2015 Fehler 2: Tests um der Tests willen • Anzeichen

    für TDT (Test Driven Test): – Code Coverage um jeden Preis – Unit Tests ohne TDD Fabian Schmengler – Pragmatisches Unit Testing 41
  42. 11.05.2015 Fehler 3: Gar keine Tests • Für Mini-Projekte kann

    manuelles Testen genügen • Automatisierung von Smoke Tests immer sinnvoll, wenn die Infrastruktur vorhanden ist – Noch einmal: (L * W) / (M * K) • Keine Test-Automatisierung => viel Zeit für manuelles Testen einplanen • Kein manuelles Testen => viel Bugfixing im Panik- Modus einplanen Fabian Schmengler – Pragmatisches Unit Testing 42
  43. 11.05.2015 Magento 2 • Eigenes Test Framework • Core ist

    mit Unit Tests versehen, aber großteils – Portiert von Magento 1 – Nicht testgetrieben entwickelt • Abhängigkeiten sind durch DI expliziter, aber – Immer noch viel zu instabil – Immer noch gibt es schwarze Löcher wie die Registry Fabian Schmengler – Pragmatisches Unit Testing 43
  44. 11.05.2015 Fabian Schmengler – Pragmatisches Unit Testing 44

  45. 11.05.2015 Wird nun alles anders? • Im Prinzip: Nein. •

    Neue Technik: Magento 2 TestFramework für Integration Tests statt EcomDev_PHPUnit oder xtest • Immerhin gibt es die Gott-Klasse „Mage“ nicht mehr Fabian Schmengler – Pragmatisches Unit Testing 45
  46. 11.05.2015 Danke für die Aufmerksamkeit! Fabian Schmengler – Pragmatisches Unit

    Testing 46
  47. 11.05.2015 Kontakt Web: www.integer-net.de Twitter: @fschmengler / @integer_net Email: fs@integer-net.de

    Fabian Schmengler – Pragmatisches Unit Testing 47
  48. 11.05.2015 Links • Brian Marick: When Should a Test Be

    Automated? http://www.exampler.com/testing-com/writings/automate.pdf • EcomDev_PHPUnit: https://github.com/EcomDev/EcomDev_PHPUnit • xtest: http://xtest-mage.com/ • Travis CI: https://travis-ci.org/ / https://travis-ci.com/ • Mage Test Stand: https://github.com/AOEpeople/MageTestStand • IntegerNet_Anonymizer: https://github.com/integer-net/Anonymizer • Humbug: https://github.com/padraic/humbug Fabian Schmengler – Pragmatisches Unit Testing 48