real life tdd

Post on 10-May-2015

185 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

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

1

Real Life TDD

Techniken und Tools für wartbaren Testcode

Real Life TDD, 15. Januar 2014

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

3

Einführung

Real Life TDD, 15. Januar 2014

4

Test-Driven-Development a

Real Life TDD, 15. Januar 2014

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

6

Test-Driven-Development a

Real Life TDD, 15. Januar 2014

• Wie?

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.

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

9

Tools

Real Life TDD, 15. Januar 2014

10

Tools

1. JUnit2. Mockito3. Hamcrest4. Powermock

Real Life TDD, 15. Januar 2014

11

JUnit

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

Real Life TDD, 15. Januar 2014

12

JUnit

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

Real Life TDD, 15. Januar 2014

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

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

15

Keeping It Clean

Real Life TDD, 15. Januar 2014

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

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?

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

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

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

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

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

23

Literatur

Real Life TDD, 15. Januar 2014

24

Literatur

Real Life TDD, 15. Januar 2014

25

Danke!

Real Life TDD, 15. Januar 2014

top related