real life tdd

25
Real Life TDD Techniken und Tools für wartbaren Testcode Real Life TDD, 15. Januar 2014 1

Upload: daniel-rosowski

Post on 10-May-2015

183 views

Category:

Technology


0 download

DESCRIPTION

Viele Softwareprojekte starten mittlerweile mit dem Anspruch testgetrieben entwickelt zu werden. Der Anfang gestaltet sich in der Regel einfach und die Entwickler erkennen schnell die Vorzüge von TDD. Leider kommt es in vielen Fällen im Laufe der Zeit dazu, dass die Wartung der Tests mehr und mehr Zeit in Anspruch nimmt. Die Entwickler sind frustriert und TDD wird vom Management als „zu wartungsintensiv“ abgewiesen. Ziel dieses Vortrags soll es sein, dort einzusteigen wo Tutorials und Einsteigerseminare und -bücher aufhören. Wir wollen dem erfahrenen Entwickler Tools und Vorgehensweisen an die Hand zu geben, um diesem „Wartungsalbtraum“ zu entgehen. Das echte Leben ist komplexer als das Taschenrechnerbeispiel!

TRANSCRIPT

Page 1: Real Life TDD

1

Real Life TDD

Techniken und Tools für wartbaren Testcode

Real Life TDD, 15. Januar 2014

Page 2: Real Life TDD

2

Agenda

• Einführung– (sehr) kurze Einführung in TDD– Test Doubles– Vorstellung der Tools

• Keeping it clean– Best Practices– 10 Dinge um dein Design zu zerstören– Auf die Tests hören

Real Life TDD, 15. Januar 2014

Page 3: Real Life TDD

3

Einführung

Real Life TDD, 15. Januar 2014

Page 4: Real Life TDD

4

Test-Driven-Development a

Real Life TDD, 15. Januar 2014

Page 5: Real Life TDD

5

Test-Driven-Development a

Real Life TDD, 15. Januar 2014

• Warum?– Korrektheit– Sauberes Design– Seelenfrieden

• Wann?– Test first!– TDD ist Teil des Entwicklungsprozesses und sollte

immer mit eingeplant werden

Page 6: Real Life TDD

6

Test-Driven-Development a

Real Life TDD, 15. Januar 2014

• Wie?

Page 7: Real Life TDD

7

Test-Driven-Development a

Real Life TDD, 15. Januar 2014

1. Test schlägt fehl– Vor der Funktionalität kommt der Test• assertEquals(„foobar“, cut.toString())

2. Test erfolgreich– Die Annahmen im Test werden bedient• return „foobar“

3. Refaktorierung– Vereinfachen, Konsolidieren, Verschieben etc.

Page 8: Real Life TDD

8

Test Doubles

• „generic term for any kind of pretend object used in place of a real object for testing purposes“ – Martin Fowler [1]– Dummy– Fake– Stubs– Mocks

• Was wollen wir testen?– State oder Behaviour

Real Life TDD, 15. Januar 2014

Page 9: Real Life TDD

9

Tools

Real Life TDD, 15. Januar 2014

Page 10: Real Life TDD

10

Tools

1. JUnit2. Mockito3. Hamcrest4. Powermock

Real Life TDD, 15. Januar 2014

Page 11: Real Life TDD

11

JUnit

• Einführung von Rules seit JUnit 4.7 [2]• Vordefinierte Regeln– TemporaryFolder– Timeout– ExpectedException– ErrorCollector– TestName

Real Life TDD, 15. Januar 2014

Page 12: Real Life TDD

12

JUnit

• Eigene JUnit Regeln erstellen– Interface TestRule implementieren– Vorhandene Templateklassen verwenden• ExternalResource• TestWatcher• Verifier

Real Life TDD, 15. Januar 2014

Page 13: Real Life TDD

13

• Mocking framework• Warum Mockito?– Einfaches Erzeugen von Mock Objekten– Annotationen erleichtern das Setup– Einfache API, compile-safe– Unterstützt viele Anwendungsfälle• Mocks, Stubs, Partial-Mocks, Spies

– Hervorragende Dokumentation

Real Life TDD, 15. Januar 2014

Mockito

Page 14: Real Life TDD

14

Hamcrest

• Matcher Bibliothek [3]• Komplexe assertXY Ausdrücke werden häufig

unleserlich• Seit JUnit 4.4: – assertThat(T actual,

org.hamcrest.Matcher<T> matcher)

Real Life TDD, 15. Januar 2014

Page 15: Real Life TDD

15

Keeping It Clean

Real Life TDD, 15. Januar 2014

Page 16: Real Life TDD

16

Keeping It Clean public void testCreatePurchaseOrder() throws Exception { Map<String, Object> ctx = FastMap.newInstance(); ctx.put( "partyId", "Company" ); ctx.put( "orderTypeId", "PURCHASE_ORDER" ); ctx.put( "currencyUom", "USD" ); ctx.put( "productStoreId", "9000" );

GenericValue orderItem = delegator.makeValue( "OrderItem", UtilMisc.toMap( "orderItemSeqId", "00001", "orderItemTypeId", "PRODUCT_ORDER_ITEM", "prodCatalogId", "DemoCatalog", "productId", "GZ-1000", "quantity", new BigDecimal( "2" ), "isPromo", "N" ) ); orderItem.set( "unitPrice", new BigDecimal( "1399.5" ) ); orderItem.set( "unitListPrice", BigDecimal.ZERO ); orderItem.set( "isModifiedPrice", "N" ); orderItem.set( "statusId", "ITEM_CREATED" ); List<GenericValue> orderItems = FastList.newInstance(); orderItems.add( orderItem ); ctx.put( "orderItems", orderItems );

GenericValue orderContactMech = delegator.makeValue( "OrderContactMech", UtilMisc.toMap( "contactMechPurposeTypeId", "SHIPPING_LOCATION", "contactMechId", "9000" ) ); List<GenericValue> orderContactMechs = FastList.newInstance(); orderContactMechs.add( orderContactMech ); ctx.put( "orderContactMechs", orderContactMechs );

GenericValue orderItemContactMech = delegator.makeValue( "OrderItemContactMech", UtilMisc.toMap( "contactMechPurposeTypeId", "SHIPPING_LOCATION", "contactMechId", "9000", "orderItemSeqId", "00001" ) ); List<GenericValue> orderItemContactMechs = FastList.newInstance(); orderItemContactMechs.add( orderItemContactMech ); ctx.put( "orderItemContactMechs", orderItemContactMechs );

Real Life TDD, 15. Januar 2014

Page 17: Real Life TDD

17

Keeping It Clean GenericValue orderItemShipGroup = delegator.makeValue( "OrderItemShipGroup", UtilMisc.toMap( "carrierPartyId", "UPS", "contactMechId", "9000", "isGift", "N", "maySplit", "N", "shipGroupSeqId", "00001", "shipmentMethodTypeId", "NEXT_DAY" ) ); orderItemShipGroup.set( "carrierRoleTypeId", "CARRIER" ); List<GenericValue> orderItemShipGroupInfo = FastList.newInstance(); orderItemShipGroupInfo.add( orderItemShipGroup ); ctx.put( "orderItemShipGroupInfo", orderItemShipGroupInfo );

List<GenericValue> orderTerms = FastList.newInstance(); ctx.put( "orderTerms", orderTerms );

List<GenericValue> orderAdjustments = FastList.newInstance(); ctx.put( "orderAdjustments", orderAdjustments );

ctx.put( "billToCustomerPartyId", "Company" ); ctx.put( "billFromVendorPartyId", "DemoSupplier" ); ctx.put( "shipFromVendorPartyId", "Company" ); ctx.put( "supplierAgentPartyId", "DemoSupplier" ); ctx.put( "userLogin", userLogin );

Map<String, Object> resp = dispatcher.runSync( "storeOrder", ctx ); orderId = (String) resp.get( "orderId" ); statusId = (String) resp.get( "statusId" ); assertNotNull( orderId ); assertNotNull( statusId ); }

Real Life TDD, 15. Januar 2014

WTF?

Page 18: Real Life TDD

18

Keeping It Clean public void testCreatePurchaseOrder() throws Exception { Map<String, Object> ctx = FastMap.newInstance(); ctx.put( "partyId", "Company" ); ctx.put( "orderTypeId", "PURCHASE_ORDER" ); ctx.put( "currencyUom", "USD" ); ctx.put( "productStoreId", "9000" );

GenericValue orderItem = delegator.makeValue( "OrderItem", UtilMisc.toMap( "orderItemSeqId", "00001", "orderItemTypeId", "PRODUCT_ORDER_ITEM", "prodCatalogId", "DemoCatalog", "productId", "GZ-1000", "quantity", new BigDecimal( "2" ), "isPromo", "N" ) ); orderItem.set( "unitPrice", new BigDecimal( "1399.5" ) ); orderItem.set( "unitListPrice", BigDecimal.ZERO ); orderItem.set( "isModifiedPrice", "N" ); orderItem.set( "statusId", "ITEM_CREATED" ); List<GenericValue> orderItems = FastList.newInstance(); orderItems.add( orderItem ); ctx.put( "orderItems", orderItems );

GenericValue orderContactMech = delegator.makeValue( "OrderContactMech", UtilMisc.toMap( "contactMechPurposeTypeId", "SHIPPING_LOCATION", "contactMechId", "9000" ) ); List<GenericValue> orderContactMechs = FastList.newInstance(); orderContactMechs.add( orderContactMech ); ctx.put( "orderContactMechs", orderContactMechs );

GenericValue orderItemContactMech = delegator.makeValue( "OrderItemContactMech", UtilMisc.toMap( "contactMechPurposeTypeId", "SHIPPING_LOCATION", "contactMechId", "9000", "orderItemSeqId", "00001" ) ); List<GenericValue> orderItemContactMechs = FastList.newInstance(); orderItemContactMechs.add( orderItemContactMech ); ctx.put( "orderItemContactMechs", orderItemContactMechs );

GenericValue orderItemShipGroup = delegator.makeValue( "OrderItemShipGroup", UtilMisc.toMap( "carrierPartyId", "UPS", "contactMechId", "9000", "isGift", "N", "maySplit", "N", "shipGroupSeqId", "00001", "shipmentMethodTypeId", "NEXT_DAY" ) ); orderItemShipGroup.set( "carrierRoleTypeId", "CARRIER" ); List<GenericValue> orderItemShipGroupInfo = FastList.newInstance(); orderItemShipGroupInfo.add( orderItemShipGroup ); ctx.put( "orderItemShipGroupInfo", orderItemShipGroupInfo );

List<GenericValue> orderTerms = FastList.newInstance(); ctx.put( "orderTerms", orderTerms );

List<GenericValue> orderAdjustments = FastList.newInstance(); ctx.put( "orderAdjustments", orderAdjustments );

ctx.put( "billToCustomerPartyId", "Company" ); ctx.put( "billFromVendorPartyId", "DemoSupplier" ); ctx.put( "shipFromVendorPartyId", "Company" ); ctx.put( "supplierAgentPartyId", "DemoSupplier" ); ctx.put( "userLogin", userLogin );

Map<String, Object> resp = dispatcher.runSync( "storeOrder", ctx ); orderId = (String) resp.get( "orderId" ); statusId = (String) resp.get( "statusId" ); assertNotNull( orderId ); assertNotNull( statusId ); }

Real Life TDD, 15. Januar 2014

Page 19: Real Life TDD

19

Best Practices

• Eine Testklasse pro Class-under-Test• Testname soll ausdrücken was getestet wird• Ein assert/verify pro Test• Unabhängige Tests• Blitzschnell!

• Tipps:– Eclipse: Import von statischen Methoden– Code Coverage (z.B. CodePro)

Real Life TDD, 15. Januar 2014

Page 20: Real Life TDD

20

10 Dinge um dein Design zu zerstören

1. Services da instanziieren wo sie gebraucht werden2. Law of Demeter Verletzung3. Logik im Constructor4. Global State5. Singletons6. Statische Methoden7. Implementierung vererben8. Polymorphismus nachprogrammieren9. Services und Values vermischen10. Mehr als eine Sache tun (Separation of Concerns) [4]Real Life TDD, 15. Januar 2014

Page 21: Real Life TDD

21

Auf die Tests hören

1. Schwer zu mockende Objekte2. Mocken von konkreten Klassen3. Mocken von Value Objekten4. Aufgeblähter Constructor5. Zu viele Erwartungen

Real Life TDD, 15. Januar 2014

Page 22: Real Life TDD

22

Quellen

• Links– [1] http://martinfowler.com/articles/mocksArentStubs.html– [2] http://marcphilipp.tumblr.com/post/14610767542/junit-

rules– [3] http://stefanroock.blogspot.de/2008/03/junit-44-

assertthat.html– [4] http://misko.hevery.com/2008/07/30/top-10-things-

which-make-your-code-hard-to-test/• Code

– https://github.com/marcphilipp/junit-rules– https://github.com/smartsquare/RealLifeTDD

Real Life TDD, 15. Januar 2014

Page 23: Real Life TDD

23

Literatur

Real Life TDD, 15. Januar 2014

Page 24: Real Life TDD

24

Literatur

Real Life TDD, 15. Januar 2014

Page 25: Real Life TDD

25

Danke!

Real Life TDD, 15. Januar 2014