AdvancedTdd

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    2 Favorites

    AdvancedTdd - Presentation Transcript

    1. Fortgeschrittene Techniken der Testgetriebenen Entwicklung JAX Powerworkshop, 23.04.2007 Tammo Freese, Johannes Link
    2. Agenda • Testgetriebene Entwicklung im Überblick • Akzeptanztests mit FIT • Unit Tests mit JUnit/Mockobjekte mit EasyMock • Legacy Code und Code Coverage • Testen von GUIs und Web-Applikationen • Testen mit Skriptsprachen • Diskussion
    3. Motivation • Software hat Geschäftswert durch zwei Qualitäten • Funktionale Qualität (Funktionalität, Fehlerfreiheit) • Strukturelle Qualität (Design/Codestruktur für Weiterentwicklung) • Hinzufügen neuer Funktionalität gefährdet funktionale und strukturelle Qualität • Verbesserung der strukturellen Qualität gefährdet die funktionale Qualität
    4. Testgetriebene Entwicklung • Testgetriebene Programmierung: Motiviere jede Verhaltensänderung am Code durch einen automatisierten Test (ständige Absicherung der funktionalen Qualität) • Refactoring: Vereinfache das Code-Design soweit wie möglich (funktionale Qualität ist durch Tests abgesichert) • (Häufige Integration: Integriere den Code so häufig wie nötig)
    5. Entwickler schreiben Unit Tests • testen Komponenten des Systems in Isolation • geben uns konkretes Feedback • ermöglichen sichere Änderungen • sichern Erhalt der vorhandenen Funktionalität • müssen bei jeder Integration zu 100% laufen • können funktionale Tests auf Systemebene nicht ersetzen!
    6. Kunden spezifizieren Akzeptanztests • testen das System überwiegend als Ganzes • geben unseren Kunden Vertrauen in die gelieferte Software • klären die Anforderungen frühzeitig an konkreten Beispielen • machen den Projektfortschritt sichtbar • müssen vom Kunden erstellt und gepflegt werden können • Unsere Aufgabe ist es, den entsprechenden Rahmen für ihre Automatisierung zu schaen!
    7. Akzeptanztests mit FIT • FIT: Framework for Integrated Test • Zum Schreiben und Ausführen automatischer Akzeptanztests • Testdaten werden tabellarisch erstellt (in HTML, mit Excel oder im Wiki) • Anbindung ans System in Java • Portierung für aktuelle Sprachen verfügbar • http://fit.c2.com
    8. Drei Fixture-Klassen • ColumnFixture testet Ein-/Ausgabewertemengen. • ActionFixture spielt ein Benutzerszenario durch und ist deshalb gut für GUI-orientierte Tests geeignet. • RowFixture prüft eine Ergebnismenge von Objekten mit ihren Attributen.
    9. Column Fixture (1)
    10. Column Fixture (2) import fit.ColumnFixture; public class AccountTransferFixture extends ColumnFixture { public String sourceAccount; public String targetAccount; public double amountTransferred; public boolean wasTransactionSuccessful() throws AccountException { return false; } }
    11. Action Fixture (1)
    12. Action Fixture (2) import fit.ActionFixture; public class AccountAdministrationFixture extends ActionFixture { public void newAccount() {} public void customerName(String name) {} public String accountNumber() { return "0"; } public void deposit(double money) throws AccountException { } public double balance() { return 0; } }
    13. Row Fixture (1) public class AccountListingItem { public String accountNumber; public double balance() { return 0; } public String customerName() { return "dummy"; } }
    14. Row Fixture (2) import fit.RowFixture; public class AccountListingFixture extends RowFixture { public Object[] query() throws Exception { List result = new ArrayList(); return result.toArray(); } public Class getTargetClass() { return AccountListingItem.class; } }
    15. Domänenspezifische Fixtures • wenn keine Fixture-Grundart so richtig passt • Erweiterungen von fit.Fixture über Hook- Methoden: • doTables(Parse tables) • doTable(Parse table) • doRows(Parse rows) • doRow(Parse row) • doCells(Parse cells) • doCell(Parse cell, int index)
    16. Übung 1: Akzeptanztestgetriebene Entwicklung • Bringen Sie die Testfälle in acceptance-tests/RichCalc.html schrittweise zum laufen! • (TaschenrechnerFixture.java ist eine ColumnFixture)
    17. Nachlese Übung 1 • Probleme bei akzeptanztestgetriebener Entwicklung • (Zu) große Schritte: Es dauert oft zu lange, einen Test auf grün zu bringen • Keine Möglichkeit, Design zu erzwingen • Keine Möglichkeit, Module isoliert ins Leben zu testen • Kombinatorische Explosion • Lösung: Unit Tests • Viele kleine Unit Tests schreiben • Jeden Akzeptanztest schrittweise erfüllen
    18. JUnit - Testframework für Java • Java-Framework zum Schreiben und Ausführen automatischer Unit Tests • Tests werden in Java codiert • Ist auch für zahlreiche andere Programmiersprachen erhältlich • http://junit.org
    19. Beispiel: Test import org.junit.Test; import static org.junit.Assert.*; public class EuroTest { @Test public void cents() { Euro two = new Euro(2.00); assertEquals(200, two.getCents()); } }
    20. Anatomie eines Testfallklasse • Testfallmethoden @Test public void ...() • Verwendung der statisch importierten Methoden assert...() und fail() • Fehlschlag von assert...() beendet den Testfall • Instanzvariablen für Testobjekte • @Before public void ...() zum Aufbau von Testobjekten und Testressourcen • @After public void ...() zur Ressourcenfreigabe
    21. Beispiel: Test Suite import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(value = Suite.class) @SuiteClasses( { CalculatorTest.class }) public class AllTests { }
    22. Eclipse: JUnit Testrunner
    23. Test/Code/Refactor – Zyklus JUnit: Failure JUnit: OK
    24. Test/Code/Refactor – Schritte • grün-rot: Schreibe einen Test, der zunächst fehlschlagen sollte. Schreibe gerade soviel Code, dass der Test fehlschlägt. • rot-grün: Schreibe gerade soviel Code, dass alle Tests laufen. • grün-grün: Eliminiere Duplikation und andere üble Codegerüche.
    25. Programmierzug grün - rot • Programming by Intention: Der Test verwendet die zu testende Funktion, als wäre sie schon realisiert. • Die Klassenschnittstelle wird im Test aus der Perspektive eines Verwenders entworfen. • Jeder Test geht einen kleinen Schritt. • Den Test zunächst fehlschlagen zu sehen, testet den Test selbst.
    26. Programmierzug rot - grün • Nur soviel Code schreiben, wie die Tests erfordern. • Mehr wäre weder durch die Tests spezifiziert, noch abgesichert. • Erst weitere Tests beweisen, dass die Lösung eventuell noch zu einfach ist.
    27. Refactoringzug grün - grün • Eliminierung von Duplikation! • Verbesserungen am Design des Programms, ohne sein beobachtbares Verhalten zu ändern, • um die Verständlichkeit zu erhöhen • um Designschwächen zu beheben • um die Änderbarkeit zu erhalten.
    28. Strikte Unit Tests • Ein Unit Test soll eine Klasse oder ein Klassenteam in Isolation testen. • Probleme: • Programmeinheiten arbeiten nicht isoliert. • Aufbau der Testumgebung ist oft aufwändig. • Testen von Ausnahmesituationen • langsame Tests bei Überschreiten der Systemgrenzen
    29. Isoliertes Testen • Für die Dauer der Tests ersetzen wir Abhängigkeiten zu mitwirkenden Programmeinheiten durch die Einführung einfacher Attrappen.  • Vorteile: • Tests laufen schnell. • Auftretende Fehler sind leicht zu lokalisieren. • Notwendige Testkombinatorik ist kleiner als beim Testen mehrerer Objekte.
    30. Isoliertes Testen
    31. Mock-Objekte • Ersetzen von der zu testenden Unit verwendeten Klassen oder Schnittstellen während des Tests • Vorgehen: • Verhalten und Erwartungen spezifizieren • Unit unter Verwendung des Mockobjekts testen • korrekte Verwendung verifizieren
    32. EasyMock – Dynamische Mock-Objekte • Erzeugung eines Mock-Objekts „on the fly“: • MyInterface mock = createMock(MyInterface.class); • Aufzeichnen des erwarteten Verhaltens: • expect(mock.myMethod(42))andReturn("Y"); replay(mock); • Verifikation der Verwendung des Verhaltens: • verify(mock); • http://easymock.org/
    33. Systemgrenzen im Test • Dummies und Mocks: • Was wir zur Isolation innerhalb des Systems verwenden, funktioniert auch an Systemgrenzen. • Bei komplexen Systemgrenzen: • Fassade/Adapter zur zusätzlichen Indirektion einführen • Testen vom System zu Fassade/Adapter • Testen von Fassade/Adapter zur Systemgrenze • funktionale Tests testen das Zusammenspiel
    34. Übung 2: Isolierte Unit Tests • Bringen Sie die Testfälle in acceptance-tests/RichCalc.html schrittweise zum laufen! • Diesmal aber: • Schreiben Sie Unit Tests für die Akzeptanztests • Setzen Sie Mockobjekte ein, falls nötig • Startlinie: calculator.unittests.CalculatorTest
    35. Code Coverage • Wie gut sind unsere Tests? • Wie groß ist die Abdeckung des Codes durch unsere Tests? • http://www.cenqua.com/clover/ • http://works.dgic.co.jp/djunit/ • http://www.eclemma.org/ • Welche Code-Änderungen werden durch unsere Tests entdeckt? • http://jester.sourceforge.net/ • Metriken als Spiegel der Gewohnheit
    36. Testen von Legacy Code • Legacy Code ist Code ohne automatisierte Tests! • Michael Feathers: „Code without tests is bad code. It doesn't matter how well written it is [...] With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse.“
    37. Legacy Code Dilemma • Um Software sicher zu ändern, benötigen wir automatisierte Tests. • Um automatisierte Tests zu schreiben, müssen wir die Software ändern. • Refactoring ohne Tests unumgänglich
    38. Testen von bestehendem Code • Änderung des bestehenden Systems • neue Features • Entfernen von Bugs • Designverbesserungen • Optimierungen • Vergrößerung der Testabdeckung • Testgetriebene Entwicklung • Charakterisierende Tests • Integrationstests
    39. Störende Abhängigkeiten • Eine Klasse lässt sich nicht instanzieren • Eine Methode lässt sich nicht ausführen • Das Testergebnis lässt sich nicht ermitteln
    40. Vorgehen 1. Identifiziere Änderungspunkte 2. Finde Testpunkte 3. Brich Abhängigkeiten 4. Schreibe automatisierte Tests 5. Mache Änderung 6. Räume auf (Refactoring)
    41. Werkzeugkasten • Automatisierte Refactorings • JUnit Test-Framework • Mock-Objekte als Attrappen • Framework for Integrated Test (FIT) • Aspektorientierte Programmierung
    42. Einschleusen von Mocks • als Parameter • über testbare Unterklasse • mittels Überlagerung im Klassenpfad • mit aspektorientierter Programmierung • in C/C++ auch über Linker und Präprozessor • Jedes Testproblem lässt sich über eine weitere Indirektion lösen
    43. Meine Klasse lehnt sich gegen den Test auf • Ursachen: • Klasse kann nicht instanziert werden • Hinderliche Konstruktor-Parameter • Konstruktor hat Seiteneekte • Techniken: • Extrahiere und überschreibe Methode • Extrahiere Interface • Parametrisiere Konstruktor
    44. Extrahiere und Überschreibe (1) public class A { public class A { public A() { public A() { B b = makeB(); B b = new B(); b.doStuff(); b.doStuff(); ... ... } } protected B makeB() { } return new B(); } }
    45. Extrahiere und Überschreibe (2) public class TestableA extends A { protected B makeB() { return new DummyB(); } } public class DummyB extends B { public void doStuff() { } }
    46. Extrahiere Interface public class A { public class A { public A() { public A() { IB b = makeB(); B b = makeB(); b.doStuff(); b.doStuff(); ... ... } } protected IB makeB() { protected B makeB() { return new B(); return new B(); } } } }
    47. Parametrisiere Konstruktor public class A { public class A { private IB b; public A() { public A(IB b) { IB b = makeB(); this.b = b; b.doStuff(); b.doStuff(); ... ... } } } public A() { this(new B()); } }
    48. Weitere Techniken • Abzweigende Methode • Abzweigende Klasse • Statisch gemachte Methoden • Primitivere Parametertypen • Führe statischen Setter ein • Führe delegierende Instanz ein • Ersetze Singleton durch direkte Abhängigkeit • Extrahiere Methoden • Extrahiere Methodenobjekt
    49. Verbesserungen über die Zeit • Viele der Techniken machen das Design zunächst einmal hässlicher • Durch die ersten installierten Tests sind kleine Verbesserungen möglich • Kleine Verbesserungen lassen weitere Refactoringkandidaten erkennen • Schrittweise zu evolutionärem Design
    50. Testen von GUIs und Web-Applikationen • Wichtigster Grundsatz: Keine Logik im View • Entwurfsmuster zur Trennung von Präsentation und Geschäftslogik: • Application Facade • Model View Presenter
    51. Application Facade <<interface>> BookSearchFacade BookSearchDialog searchTitles(searchExpression:String) : List Book Title Author
    52. Application Facade mit Observer <<interface>> Observer changeOccurred() <<implements>> <<interface>> BookSearchFacade BookSearchDialog searchTitles(searchExpression:String) : List registerObserver(observer: Observer) changeOccurred() Book Title Author
    53. Model View Presenter <<interface>> <<interface>> BookSearchView BookSearchPresenter displayList(books:List) search (searchExpression:String) <<implements>> <<implements>> BookSearchDialog BookSearcher displayList(books:List) search (searchExpression:String)
    54. Testen mit Skriptsprachen • Dynamisch typisierte Skriptsprachen können das Testen vereinfachen • Beispiel Groovy: • Alle Java-Features und Bibliotheken verfügbar • Typisierung ist optional • „Duck-Typing“ ist möglich • Einfache Syntax für Blocks und Iteratoren
    55. Groovy: Vereinfachte Syntax • Viele Elemente optional: • public, Typisierung, Parameterklammern, Semikolon class CalculatorTest extends GroovyTestCase { private calculator void setUp() { calculator = new Calculator() ... } void testDisplayThreeDigits() { checkDisplay '1', '5', '9' } def checkDisplay(String[] buttons) { ... }
    56. Groovy: Closures und Collections • Einfache Blocksyntax • Mächtige Collections • Iteratoren mittels Blöcken def printer = { line -> println line } printer('Test') def buttons = ["1", "2", "3", "4"].reverse() def displayText = "" buttons.each { displayText += it } assert displayText == "4321"
    57. Groovy: Dynamisch typisierte Objekte • „Anonyme“ Objekte on the fly def display = {assert it == displayText} as IDisplay def display = [display: {println it}, enableDecimalSeparatorKey: { ... }, disableDecimalSeparatorKey: { ... } ] as IDisplay
    58. Übung 3: Wie es euch gefällt • Variante 1: Schreiben Sie mit Hilfe von Jemmy eine Taschenrechner-Fixture, die direkt die Swing-GUI bedient • Variante 2: Erweitern Sie die Groovy-Testsuite im Package groovy.calculator.unittests • Variante 3: Erweitern Sie GUI und GUI-Tests: public interface IDisplay { void display(String toDisplay); void disableDecimalSeparatorKey(); void enableDecimalSeparatorKey(); }
    59. Fragen & Diskussion
    60. Referenzen • Michael Feathers: Working Eectively with Legacy Code. Prentice Hall, 2004. • Johannes Link: Softwaretests mit JUnit - Techniken der testgetriebenen Entwicklung. Dpunkt-Verlag, 2005. • Rick Mugridge, Ward Cunningham: Fit for Developing Software. Prentice Hall, 2005. • Frank Westphal: Testgetriebene Entwicklung mit JUnit & FIT. Dpunkt-Verlag, 2005.

    + jlinkjlink, 3 years ago

    custom

    3821 views, 2 favs, 0 embeds more stats

    A power workshop during JAX 2007 on advanced techni more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 3821
      • 3821 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 2
    • Downloads 0
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories