SlideShare ist ein Scribd-Unternehmen logo
1 von 27
Real Life TDD
Techniken und Tools für wartbaren
Testcode
Real Life TDD, 15. Januar 2014 1
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 2
Einführung
Real Life TDD, 15. Januar 2014 3
Test-Driven-Development a
Real Life TDD, 15. Januar 2014 4
Test-Driven-Development a
Real Life TDD, 15. Januar 2014 5
• Warum?
– Korrektheit
– Sauberes Design
– Ermöglicht Refactorings
– Seelenfrieden 
• Wann?
– Test first!
– TDD ist Teil des Entwicklungsprozesses und sollte
immer mit eingeplant werden
Test-Driven-Development a
Real Life TDD, 15. Januar 2014 6
• Wie?
Test-Driven-Development a
Real Life TDD, 15. Januar 2014 7
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. Refactoring
– Vereinfachen, Konsolidieren, Verschieben etc.
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 8
Tools
Real Life TDD, 15. Januar 2014 9
Tools
1. JUnit
2. Mockito
3. Hamcrest
4. AssertJ
5. Powermock
Real Life TDD, 15. Januar 2014 10
JUnit
• Einführung von Rules seit JUnit 4.7 [2]
• Vordefinierte Regeln
– TemporaryFolder
– Timeout
– ExpectedException
– ErrorCollector
– TestName
Real Life TDD, 15. Januar 2014 11
JUnit
• Eigene JUnit Regeln erstellen
– Interface TestRule implementieren
– Vorhandene Templateklassen verwenden
• ExternalResource
• TestWatcher
• Verifier
Real Life TDD, 15. Januar 2014 12
JUnit
• Parametrisierte Tests
– Kompakte, lesbare Testmethoden
– DRY Prinzip in den Tests
• Aber
– Umständlich in plain JUnit
– JUnitParams („ Parameterised tests that don't
suck“)
– Oder gleich Spock ;-)
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 14
Mockito
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
AssertJ
• Fluent Matcher Bibliothek [4]
assertThat(fellowshipOfTheRing)
.hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
Real Life TDD, 15. Januar 2014 16
Keeping It Clean
Real Life TDD, 15. Januar 2014 17
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 18
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 19
WTF?
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 20
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:
– Import von statischen Methoden
– Code Coverage (z.B. CodePro)
Real Life TDD, 15. Januar 2014 21
10 Dinge um dein Design
zu zerstören
1. Services da instanziieren wo sie gebraucht werden
2. Law of Demeter Verletzung
3. Logik im Constructor
4. Global State
5. Singletons
6. Statische Methoden
7. Implementierung vererben
8. Polymorphismus nachprogrammieren
9. Services und Values vermischen
10. Mehr als eine Sache tun (Separation of Concerns) [5]
Real Life TDD, 15. Januar 2014 22
Auf die Tests hören
1. Schwer zu mockende Objekte
2. Mocken von konkreten Klassen
3. „Wie mocke ich eine private Methode?“
4. Mocken von Value Objekten
5. Aufgeblähter Constructor
6. Zu viele Erwartungen
Real Life TDD, 15. Januar 2014 23
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://joel-costigliola.github.io/assertj/
– [5] 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 24
Literatur
Real Life TDD, 15. Januar 2014 25
Literatur
Real Life TDD, 15. Januar 2014 26
Danke!
Real Life TDD, 15. Januar 2014 27

Weitere ähnliche Inhalte

Ähnlich wie Real Life TDD

Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2dayElegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2dayMario-Leander Reimer
 
In-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und KubernetesIn-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und KubernetesQAware GmbH
 
Behaviour-Driven Development
Behaviour-Driven DevelopmentBehaviour-Driven Development
Behaviour-Driven Developmentjlink
 
Mögen die Tests mit dir sein
Mögen die Tests mit dir seinMögen die Tests mit dir sein
Mögen die Tests mit dir seincodepitbull
 
96% macoun 2013
96% macoun 201396% macoun 2013
96% macoun 2013Maxim Zaks
 
Intersys - Integration mit Spirateam (Zurich 2017)
Intersys - Integration mit Spirateam (Zurich 2017)Intersys - Integration mit Spirateam (Zurich 2017)
Intersys - Integration mit Spirateam (Zurich 2017)Adam Sandman
 
BizDataX Testdatenmanagement Konzepte
BizDataX Testdatenmanagement KonzepteBizDataX Testdatenmanagement Konzepte
BizDataX Testdatenmanagement KonzepteDragan Kinkela
 
Legacy Code refaktorisieren
Legacy Code refaktorisierenLegacy Code refaktorisieren
Legacy Code refaktorisierenHendrik Lösch
 
Einführung Software Testing und Qualitätssicherung
Einführung Software Testing und QualitätssicherungEinführung Software Testing und Qualitätssicherung
Einführung Software Testing und QualitätssicherungChristian Baranowski
 
Continuous Delivery aus Sicht des Testers
Continuous Delivery aus Sicht des TestersContinuous Delivery aus Sicht des Testers
Continuous Delivery aus Sicht des TestersQAware GmbH
 
SharePoint Testing mit Visual Studio 2012
SharePoint Testing mit Visual Studio 2012SharePoint Testing mit Visual Studio 2012
SharePoint Testing mit Visual Studio 2012daniellindemann
 
Dart (Teil I der Tour de Dart)
Dart (Teil I der Tour de Dart)Dart (Teil I der Tour de Dart)
Dart (Teil I der Tour de Dart)Nane Kratzke
 
Performance trotz Entity Framwork
Performance trotz Entity FramworkPerformance trotz Entity Framwork
Performance trotz Entity FramworkAndré Krämer
 
Feige sein! Testen im Java-EE-Umfeld
Feige sein! Testen im Java-EE-UmfeldFeige sein! Testen im Java-EE-Umfeld
Feige sein! Testen im Java-EE-Umfeldgedoplan
 
Hands-on Hystrix - Best Practices und Stolperfallen
Hands-on Hystrix - Best Practices und StolperfallenHands-on Hystrix - Best Practices und Stolperfallen
Hands-on Hystrix - Best Practices und Stolperfalleninovex GmbH
 
Lightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPALightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPAmh0708
 
130605 blog - drools
130605   blog - drools130605   blog - drools
130605 blog - droolsjava-pe
 
Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?QAware GmbH
 
Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?Josef Adersberger
 
Schnell, schneller, Quarkus!!
Schnell, schneller, Quarkus!!Schnell, schneller, Quarkus!!
Schnell, schneller, Quarkus!!gedoplan
 

Ähnlich wie Real Life TDD (20)

Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2dayElegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
Elegantes In-Memory Computing mit Apache Ignite und Kubernetes. @data2day
 
In-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und KubernetesIn-Memory Computing mit Apache Ignite und Kubernetes
In-Memory Computing mit Apache Ignite und Kubernetes
 
Behaviour-Driven Development
Behaviour-Driven DevelopmentBehaviour-Driven Development
Behaviour-Driven Development
 
Mögen die Tests mit dir sein
Mögen die Tests mit dir seinMögen die Tests mit dir sein
Mögen die Tests mit dir sein
 
96% macoun 2013
96% macoun 201396% macoun 2013
96% macoun 2013
 
Intersys - Integration mit Spirateam (Zurich 2017)
Intersys - Integration mit Spirateam (Zurich 2017)Intersys - Integration mit Spirateam (Zurich 2017)
Intersys - Integration mit Spirateam (Zurich 2017)
 
BizDataX Testdatenmanagement Konzepte
BizDataX Testdatenmanagement KonzepteBizDataX Testdatenmanagement Konzepte
BizDataX Testdatenmanagement Konzepte
 
Legacy Code refaktorisieren
Legacy Code refaktorisierenLegacy Code refaktorisieren
Legacy Code refaktorisieren
 
Einführung Software Testing und Qualitätssicherung
Einführung Software Testing und QualitätssicherungEinführung Software Testing und Qualitätssicherung
Einführung Software Testing und Qualitätssicherung
 
Continuous Delivery aus Sicht des Testers
Continuous Delivery aus Sicht des TestersContinuous Delivery aus Sicht des Testers
Continuous Delivery aus Sicht des Testers
 
SharePoint Testing mit Visual Studio 2012
SharePoint Testing mit Visual Studio 2012SharePoint Testing mit Visual Studio 2012
SharePoint Testing mit Visual Studio 2012
 
Dart (Teil I der Tour de Dart)
Dart (Teil I der Tour de Dart)Dart (Teil I der Tour de Dart)
Dart (Teil I der Tour de Dart)
 
Performance trotz Entity Framwork
Performance trotz Entity FramworkPerformance trotz Entity Framwork
Performance trotz Entity Framwork
 
Feige sein! Testen im Java-EE-Umfeld
Feige sein! Testen im Java-EE-UmfeldFeige sein! Testen im Java-EE-Umfeld
Feige sein! Testen im Java-EE-Umfeld
 
Hands-on Hystrix - Best Practices und Stolperfallen
Hands-on Hystrix - Best Practices und StolperfallenHands-on Hystrix - Best Practices und Stolperfallen
Hands-on Hystrix - Best Practices und Stolperfallen
 
Lightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPALightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPA
 
130605 blog - drools
130605   blog - drools130605   blog - drools
130605 blog - drools
 
Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?Cloud Native & Java EE: Freund oder Feind?
Cloud Native & Java EE: Freund oder Feind?
 
Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?Cloud Native und Java EE: Freund oder Feind?
Cloud Native und Java EE: Freund oder Feind?
 
Schnell, schneller, Quarkus!!
Schnell, schneller, Quarkus!!Schnell, schneller, Quarkus!!
Schnell, schneller, Quarkus!!
 

Real Life TDD

  • 1. Real Life TDD Techniken und Tools für wartbaren Testcode Real Life TDD, 15. Januar 2014 1
  • 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 2
  • 3. Einführung Real Life TDD, 15. Januar 2014 3
  • 4. Test-Driven-Development a Real Life TDD, 15. Januar 2014 4
  • 5. Test-Driven-Development a Real Life TDD, 15. Januar 2014 5 • Warum? – Korrektheit – Sauberes Design – Ermöglicht Refactorings – 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 6 • Wie?
  • 7. Test-Driven-Development a Real Life TDD, 15. Januar 2014 7 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. Refactoring – 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 8
  • 9. Tools Real Life TDD, 15. Januar 2014 9
  • 10. Tools 1. JUnit 2. Mockito 3. Hamcrest 4. AssertJ 5. Powermock Real Life TDD, 15. Januar 2014 10
  • 11. JUnit • Einführung von Rules seit JUnit 4.7 [2] • Vordefinierte Regeln – TemporaryFolder – Timeout – ExpectedException – ErrorCollector – TestName Real Life TDD, 15. Januar 2014 11
  • 12. JUnit • Eigene JUnit Regeln erstellen – Interface TestRule implementieren – Vorhandene Templateklassen verwenden • ExternalResource • TestWatcher • Verifier Real Life TDD, 15. Januar 2014 12
  • 13. JUnit • Parametrisierte Tests – Kompakte, lesbare Testmethoden – DRY Prinzip in den Tests • Aber – Umständlich in plain JUnit – JUnitParams („ Parameterised tests that don't suck“) – Oder gleich Spock ;-) Real Life TDD, 15. Januar 2014 13
  • 14. • 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 14 Mockito
  • 15. 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
  • 16. AssertJ • Fluent Matcher Bibliothek [4] assertThat(fellowshipOfTheRing) .hasSize(9) .contains(frodo, sam) .doesNotContain(sauron); Real Life TDD, 15. Januar 2014 16
  • 17. Keeping It Clean Real Life TDD, 15. Januar 2014 17
  • 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 ); Real Life TDD, 15. Januar 2014 18
  • 19. 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 19 WTF?
  • 20. 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 20
  • 21. 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: – Import von statischen Methoden – Code Coverage (z.B. CodePro) Real Life TDD, 15. Januar 2014 21
  • 22. 10 Dinge um dein Design zu zerstören 1. Services da instanziieren wo sie gebraucht werden 2. Law of Demeter Verletzung 3. Logik im Constructor 4. Global State 5. Singletons 6. Statische Methoden 7. Implementierung vererben 8. Polymorphismus nachprogrammieren 9. Services und Values vermischen 10. Mehr als eine Sache tun (Separation of Concerns) [5] Real Life TDD, 15. Januar 2014 22
  • 23. Auf die Tests hören 1. Schwer zu mockende Objekte 2. Mocken von konkreten Klassen 3. „Wie mocke ich eine private Methode?“ 4. Mocken von Value Objekten 5. Aufgeblähter Constructor 6. Zu viele Erwartungen Real Life TDD, 15. Januar 2014 23
  • 24. 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://joel-costigliola.github.io/assertj/ – [5] 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 24
  • 25. Literatur Real Life TDD, 15. Januar 2014 25
  • 26. Literatur Real Life TDD, 15. Januar 2014 26
  • 27. Danke! Real Life TDD, 15. Januar 2014 27

Hinweis der Redaktion

  1. Korrektheit = Fehlerfrei Sauberes Design durch strikte Trennung von SUT und Collaborator Seelenfrieden durch hohe Testabdeckung, erleichtert Refactorings Test first! Sonst gehen grundlegende Vorteile verloren TDD ist grundlegendes Tool und muss mit eingeplant werden
  2. Dummy Objekte die durchgereicht, aber niemals verwendet werden Fake Funktionierende Implementierung die aber nicht für den produktiven Einsatz bestimmt ist (z.B. in-memory DBs) Stub Liefert vorher definierte Antworten (z.B. FTP, E-Mail) und zeichnet ggfs Verwendung auf Mock Objekte die Erwartungen definieren und überprüft werden können (behaviour!) Test state or behaviour?!
  3. TemporaryFolder Verwalten von temporären Verzeichnissen/Dateien Timeout Globalen Timeout definieren ExpectedException Erwartete Exception, bietet zusätzliche Funktionen wie das Überprüfen der Message ErrorCollector Führt alle Asserts aus und sammelt Fehler TestName Gibt den Namen des aktuellen Tests wider
  4. ExternalResource Stellt Ressourcen in einer before() Methode bereit, räumt in after() wieder auf TestWatcher Benachrichtigung über Testausführung (reagieren auf fail, success) Verify Überprüfungen nach dem Test Code zeigen!
  5. ExternalResource Stellt Ressourcen in einer before() Methode bereit, räumt in after() wieder auf TestWatcher Benachrichtigung über Testausführung (reagieren auf fail, success) Verify Überprüfungen nach dem Test Code zeigen!
  6. Einfaches Erzeugen von Mock Objekten Es müssen keine expectations formuliert werden Annotationen Mock, InjectMocks, Captor Einfache API Statische Methoden lassen sich importieren -> sauberer testcode Echte API in Mocks -> compile-safe Mocking and verification Stubbing Argument matching Verifying exact number of invocations Stubbing void methods
  7. Vorheriger Titel: 10 Dinge um den Code untestbar zu machen Powermock schafft Abhilfe Dependencies und Collaborator werden versteckt, Abhängigkeit zu Implementierung Mocken von ganzen Objektgraphen Wissen implizit, schwer nachvollziehbar Verletzt die Unabhängigkeit von Tests Gleiche wie global State Ähnlich wie 1 Wir müssen Dinge mocken die wir gar nicht verwenden/testen wollen Häufig einfacher und lesbarer Polymorphismus zu verwenden Aufbau von solchen Hybriden meist problematisch, Values sollten immutable sein Sollte klar sein
  8. Warum ist das Objekt schwer zu mocken „Clients should not be forced to depend upon interfaces that they do not use“ Separate Klasse? Was will man mocken? Zu kompliziert? Builder! Zu viele Abhängigkeiten -> Konsolidieren! Schwer zu unterscheiden was setup ist und was getestet wird