67

Smidig 2011 TDD Workshop

Embed Size (px)

DESCRIPTION

Presentation from TDD workshop at Smidig 2011 hosted by Jonas Follesø.The slides are in Norwegian.

Citation preview

Page 1: Smidig 2011 TDD Workshop
Page 2: Smidig 2011 TDD Workshop

TDD: FRA FIZZBUZZ TIL PRAKTISK BRUK I PROSJEKT, OG HVORDAN JEG SKRIVER

TESTER

90 min workshop

Smidig 2011

Jonas Follesø

14//11

Page 3: Smidig 2011 TDD Workshop

LITT OM MEG

Scientist BEKK Trondheim

Aktiv i .NET fagmiljøetBrukt TDD siden 2006

Jonas Follesø

Page 4: Smidig 2011 TDD Workshop

LITT OM DERE?

Page 5: Smidig 2011 TDD Workshop

AGENDA

Essensen i TDD

Feedback Loops

Lesbare tester

Mocking

Builder Pattern

Satna Wish List Processor

Page 6: Smidig 2011 TDD Workshop

RØD, GRØNN, REFAKTORER FEEDBACK LOOP I TDD

Page 7: Smidig 2011 TDD Workshop

TDD GIR OSS TILBAMELEDINGER PÅ

Design(er koden

vellstrukturert)

Implementasjon

(virker koden)

Page 8: Smidig 2011 TDD Workshop

TEST OPPFØRSEL – IKKE METODER

I begynnelsen skrev vi tester som «speiler» klassen som testes

public class OrderProcessing{ public void ProcessOrder(Order order){} public void ShipOrder(Order order){}}

[TestFixture]public class OrderProcessingTest{ [Test] public void ProcessOrderTest(){}

[Test] public void ShipOrderTest(){}

[Test] public void ShipOrderTest2(){}}

Page 9: Smidig 2011 TDD Workshop

TEST OPPFØRSEL – IKKE METODER

“I found the shift from thinking in tests to thinking in behaviour so profound that I started to refer to TDD as BDD”

- Dan North, Introducing BDD blog post

Page 10: Smidig 2011 TDD Workshop

TESTDOX NAVNEKONVENSJON

Hver test er en setning med klassen som testes som implisitt subjekt

• A List holds items in the order they were added

• A List can hold multiple references to the same item

• A List throws an exception when removing an item it

doesn’t hold

Page 11: Smidig 2011 TDD Workshop

TESTKLASSE SKREVET ETTER TESTDOX KONVENSJON[TestFixture]public class ListTest{

[Test] public void Holds_items_in_the_order_they_were_added() { }

[Test] public void Can_hold_multiple_references_to_the_same_item() { }

[Test] public void Throws_exception_when_removing_an_item_it_doesnt_hold() { }

}

Page 12: Smidig 2011 TDD Workshop

KLASSEVARIABLER I BUNNEN AV KLASSEN

[TestFixture]public class OrderProcessingTest{ [Test] public void Should_generate_shipping_statement_for_order() { }

[SetUp] public void Given_we_have_an_order_processor() { emailService = new EmailMock(); processor = new OrderProcessing(emailService); }

private OrderProcessing processor; private EmailMock emailService;}

Page 13: Smidig 2011 TDD Workshop

ARRANGE, ACT, ASSERT

[Test]public void Should_read_inbox_location(){ string[] parameters = {@"c:\inbox", ""};

var initParameter = new InitParameters(parameters);

initParameter.Inbox.Should().Be(@"c:\inbox");}

Page 14: Smidig 2011 TDD Workshop

EKSEMPLER PÅ ASSERTS

[Test]public void Standard_NUnit_Asserts(){ string input = "Merry Christmas"; Assert.That(input, Is.EqualTo("Merry Christmas"));}

[Test]public void NUnit_Should_Asserts(){ string input = "Merry Christmas"; input.Should(Be.EqualTo("Merry Christmas"));}

[Test]public void Fluent_Asserts(){ string input = "Merry Christmas"; input.Should().Be("Merry Christmas");}

Page 15: Smidig 2011 TDD Workshop

NUNIT SHOULD ASSERTS

public class Be : Is { public Be() { } }public class Have : Has { public Have() { } }public class Contain : Contains { public Contain() { } }

public static partial class ShouldExtensions{ public static void Should(this object o, IResolveConstraint constraint) { Assert.That(o, constraint); } public static void ShouldNot(this object o, Constraint constraint) { Assert.That(o, new NotOperator().ApplyPrefix(constraint)); }}

Page 16: Smidig 2011 TDD Workshop

FLUENT ASSERTS

input.Should().Be("Merry Christmas");

public static class MyFluentAsserts{ public static StringAsserts Should(this string text) { return new StringAsserts(text); }}

public class StringAsserts{ private readonly string _text;

public StringAsserts(string text) { _text = text; }

public void Be(string otherString) { Assert.That(_text, Is.EqualTo(otherString)); }}

Page 17: Smidig 2011 TDD Workshop

STANDARD NUNIT ASSERTS

[Test]public void Standard_NUnit_Asserts(){ string input = "Merry Christmas"; Assert.That(input, Is.EqualTo("Merry Christmas"));}

[Test]public void NUnit_Should_Asserts(){ string input = "Merry Christmas"; input.Should(Be.EqualTo("Merry Christmas"));}

[Test]public void Fluent_Asserts(){ string input = "Merry Christmas"; input.Should().Be("Merry Christmas");}

Page 18: Smidig 2011 TDD Workshop

ET EKSTRA STEG I TDD-PROSESSEN

Page 19: Smidig 2011 TDD Workshop

IKKE LESBAR FEILMELDING

[Test]public void Non_readable_diagnostics(){ string input = "Merry Christmas"; Assert.True(input == "Merry christmas");}

------ Test started: Assembly: SantaWorkshop.Tests.dll ------

Test 'SantaWorkshop.Tests.TempTest.Non_readable_diagnostics' failed: Expected: True But was: FalseTempTest.cs(35,0): at SantaWorkshop.Tests.TempTest.Non_readable_diagnostics()

0 passed, 1 failed, 0 skipped, took 0,55 seconds (NUnit 2.5.5).

Page 20: Smidig 2011 TDD Workshop

LESBAR FEILMELDING[Test]public void Readable_diagnostics(){ string input = "Merry Christmas"; Assert.That(input, Is.EqualTo("Merry christmas"));}

------ Test started: Assembly: SantaWorkshop.Tests.dll ------

Test 'SantaWorkshop.Tests.TempTest.Readable_diagnostics' failed: String lengths are both 15. Strings differ at index 6. Expected: "Merry christmas" But was: "Merry Christmas" -----------------^TempTest.cs(42,0): at SantaWorkshop.Tests.TempTest.Readable_diagnostics()

0 passed, 1 failed, 0 skipped, took 0,37 seconds (NUnit 2.5.5).

Page 21: Smidig 2011 TDD Workshop

ET EKSTRA STEG I TDD-PROSESSEN

Page 22: Smidig 2011 TDD Workshop

FOKUS PÅ LESBARHET

Som utvikler bruker vi langt mere tid på å lese kode enn å skrive kode

• Lett å forstå hva koden gjør gjennom testnavn

• Lett å forstå hvordan bruke koden gjennom test implementasjon

• Lett å forstå hvorfor en test feiler når koden endrer seg

• Lett å forstå hvordan koden er implementert gjennom kontinuerlig refaktorering og fokuserte/små klasser

Page 23: Smidig 2011 TDD Workshop

TESTENE BLIR LEVENDE DOKUMENTASJON

Page 24: Smidig 2011 TDD Workshop

LESBARHET

Testkode beskriver hva koden

gjør. Konkret i verdiene som

brukes som eksempler på hva

koden gjør, og til å verifisere at

den faktisk gjør det, men

abstrakt i hvordan koden

fungerer

Produksjonskode er absrrakt i

verdiene som brukes, men

konkret i hvordan den får

jobben gjort.

Page 25: Smidig 2011 TDD Workshop

HVOR BEGYNNER VI?

Tester fra utsiden og inn for å hele tiden fokusere på funksjonalitet som gir verdi

Begynner å teste et «skjelet» som tar for seg den enkleste funksjonaliteten som gir mening

Skrive en ende-til-ende akseptansetest som vil «tvinge oss» til å begynne på den faktiske implementasjonen

TEST ET «VANDRENDE SKJELETT»

Page 26: Smidig 2011 TDD Workshop

AKSEPTANSETEST SOM YTRE FEEDBACK LOOP

Page 27: Smidig 2011 TDD Workshop

ATDD I ET STØRRE PERSPEKTIV

Page 28: Smidig 2011 TDD Workshop

UNIT, INTEGRATION & ACCEPTANCE TEST

• Tester vår kode

• Uavhengig av miljø

• Kjører raskt

• Tester hvordan vår kode integrerer med kode vi ikke kontrollerer

• Kan røre miljø (filsystem, meldingskø)

• Tester systemet ende-til-ende

• Fokus på brukerhistorier

• Bruker så realistisk miljø som mulig

Unit TestIntegration

TestAcceptance

Test

Page 29: Smidig 2011 TDD Workshop

CASE: SANTA WISHLIST PROCESSOR ITERASJON 1

• Julenissen mottar store mengder ønskelister til jul.

• Ønsker system for å effektivisere behandlingen av disse.

• Systemet skal være batch-basert, og lese innkommende ønskelister som skal gjøres om til utgående pakke- og leveranselister

• Skal kjøre som en Console Application som f.eks kan settes opp som en scheduled task

• Første iterasjon gir alle barn det de har ønsket seg aller mest

Page 30: Smidig 2011 TDD Workshop

CASE: SANTA WISHLIST PROCESSOR INPUT/OUTPUT FORMAT

Input filformat:

Jonas Follesø (28)Wesselsgate 19, 7043 Trondheim

Nokia Lumia 800

iPhone 4S

iPad 2

Output filformat:

Nokia Lumia 800 (Jonas Follesø, Wesselsgate 19, 7043 Trondheim)

iPhone 4S (Ola Nordman, Kirkeveien 1, 1000 Oslo)

Page 31: Smidig 2011 TDD Workshop

KLASSISK TDD VS «LONON SCHOOL»-TDD

Verifisering av

oppførsel

Verifisering av tilstand

Page 32: Smidig 2011 TDD Workshop

KLASSISK TDD

Kent Beck’s «Test-driven Development By Example»

En algoritme oppdages en test av gangen.

Tester tilstanden til systemet etter

Page 33: Smidig 2011 TDD Workshop

ROMERTALL I KLASSISK TDD

Stegene og testene for å finne romertall for et gitt tall kan se slik ut:

• Test 1: I => return «I»

• Test 2: II => if (number == 1) return «I» else return «II»

• Test 3: III => while (number > 0) concat «I» to result and decrement number

Verifisering av tilstand

Page 34: Smidig 2011 TDD Workshop

KLASSISK TDD

Vi lager en mer generell løsning en test av gangen, slik at vi til slutt ender opp med en enkel, elegant løsning som håndterer alle testene opp til nå

Page 35: Smidig 2011 TDD Workshop

«LONDON STYLE»-TDD / MOCKIST TDD

«Growing Object Oriented Software Guided by Tests» den definitive boka.

Fokus på:

• Roller

• Ansvarsområder

• Interaksjon

Verifisering av

oppførsel

Page 36: Smidig 2011 TDD Workshop

ET NETTVERK AV OBJEKTER

Page 37: Smidig 2011 TDD Workshop

HVOR VI ØNSKER Å TESTE ETT OBJEKT FOR SEG

Page 38: Smidig 2011 TDD Workshop

HVOR VI ØNSKER Å TESTE ETT OBJEKT FOR SEG

Page 39: Smidig 2011 TDD Workshop

TESTING MED MOCK OBJEKTER

Verifisering av

oppførsel

Page 40: Smidig 2011 TDD Workshop

TESTING MED MOCK OBJEKTER

[Test]public void Should_send_email_when_order_is_processed(){ var emailMock = new Mock<ISendEmail>();

emailMock.Setup(e => e.SendMessage(It.Is<Message>(m => m.To == "[email protected]"))) .Returns(true) .Verifiable("Did not send e-mail to customer as expected");

var orderProcessing = new OrderProcessing(emailMock.Object); var order = new Order {CustomerEmail = "[email protected]"}; orderProcessing.ProcessOrder(order); emailMock.Verify();}

Page 41: Smidig 2011 TDD Workshop

TESTING MED MOCK OBJEKTER

Test 'Should_send_email_when_order_is_processed' failed: Moq.MockVerificationException : The following setups were not matched:Did not send e-mail to customer as expected: ISendEmail e => e.SendMessage(It.Is<Message>(m => m.To == "[email protected]"))

Page 42: Smidig 2011 TDD Workshop

TESTING MED HÅNDSKREVET MOCK

[Test]public void Should_send_email_when_order_is_processed(){ var emailMock = new EmailMock(); var orderProcessing = new OrderProcessing(emailMock);

var order = new Order {CustomerEmail = "[email protected]"}; orderProcessing.ProcessOrder(order);

emailMock.SendMessageWasCalled.Should().BeTrue("Should send e-mail to customer"); emailMock.MessageThatWasSent.To.Should().Be("[email protected]");}

Page 43: Smidig 2011 TDD Workshop

TESTING MED HÅNDSKREVET MOCK

Test ‘Should_send_email_when_order_is_processed' failed: Expected True because Should send e-mail to customer, but found False.

Page 44: Smidig 2011 TDD Workshop

EMAILMOCK KLASSE

public class EmailMock : ISendEmail{ public Message MessageThatWasSent; public bool SendMessageWasCalled; public Exception ExceptionToThrow; public bool ReturnValue;

public bool SendMessage(Message message) { SendMessageWasCalled = true; MessageThatWasSent = message;

if(ExceptionToThrow != null) throw ExceptionToThrow;

return ReturnValue; }}

Page 45: Smidig 2011 TDD Workshop

MOCK RAMMEVERK ELLER HÅNDSKREVNE MOCKS?Mock Rammeverk

• Mulighet for avansert matching på parametere

• Slipper å skrive enge mock klasser

• Presise feilmeldinger på hvilke expectations som ikke ble møtt

Håndskrevne Mocks

• Lett å forstå for «nybegynnere»

• Lett å debugge gjennom koden

Page 46: Smidig 2011 TDD Workshop

TESTING MED MOCK OBJEKTER

Tester hvordan vårt objekt sammarbeider med objektene rundt seg

Gjennom å skrive tester på denne måten designer vi både det inngående og utgående grensesnittet til klassen

Inngående grensesnitt er metoder på klassen

Utgående grensesnitt er avhengigheter, og metodene vi kaller på disse klassene

Verifisering av

oppførsel

Page 47: Smidig 2011 TDD Workshop

TESTING MED MOCK OBJEKTER

Verifisering av

oppførsel

Page 48: Smidig 2011 TDD Workshop

KLASSISK TDD VS «LONON SCHOOL»-TDD

Verifisering av

oppførsel

Verifisering av tilstand

Page 49: Smidig 2011 TDD Workshop

DRIVKREFTER FOR DESIGN

Higher level of

abstractions

Seperation of concerns

Page 50: Smidig 2011 TDD Workshop

PORTS AND ADAPTERS

Integration /Acceptanc

e Test

Unit Test

Page 51: Smidig 2011 TDD Workshop

ARKITEKTUREN OG DESIGNET SOM VIL VOKSE FRAM

Integration /Acceptanc

e Test

Unit Test

Page 52: Smidig 2011 TDD Workshop

OPPSETT AV TESTDATA

[Test]public void Should_generate_shipping_statement_for_order(){ var order = new Order { Customer = new Customer { Name = "Jonas Follesø", Address = new Address { City = "Lakselv", PostalCode = 9700 } }, Lines = new List<OrderLine> { new OrderLine { Product = "Some product", Quantity = 1 }, new OrderLine { Product = "Some other product", Quantity = 2 } } };

var orderProcessing = new OrderProcessing(); orderProcessing.ProcessOrder(order);}

Page 53: Smidig 2011 TDD Workshop

PROBLEMER MED TESTDATA

• Vanskelig å se hvilke felter som har betyrning for testen

• Testene blir mer skjøre mot endringer i datastruktur

• Testene ikke like enkle å lese

• Bruker vi «immutable value types» (DDD) må feltene settes i konstruktør

Page 54: Smidig 2011 TDD Workshop

TESTDATA MED BUILDER PATTERN

[Test]public void Should_generate_shipping_statement_for_order(){ Order order = Build

.AnOrder() .ForCustomer(CustomerBuilder.ACustomer()) .WithLine("Some product", quantity: 1) .WithLine("Some other product", quantity: 2);

var orderProcessing = new OrderProcessing(); orderProcessing.ProcessOrder(order);}

Page 55: Smidig 2011 TDD Workshop

BUILDER PATTERN IMPLEMENTASJONpublic class CustomerBuilder{ private Customer customer;

public static CustomerBuilder ACustomer() { return new CustomerBuilder(); }

private CustomerBuilder() { customer = new Customer(); customer.Name = "John Doe"; }

public static implicit operator Customer(CustomerBuilder builder) { return builder.customer; }}

Page 56: Smidig 2011 TDD Workshop

BUILDER PATTERN IMPLEMENTASJON

public CustomerBuilder WithName(string name){ customer.Name = name; return this;}

public CustomerBuilder WithAddress(Address address){ customer.Address = address; return this;}

Page 57: Smidig 2011 TDD Workshop

BUILDER PATTERN FACTORY

public static class Build{ public static CustomerBuilder ACustomer() { return CustomerBuilder.ACustomer(); }

public static OrderBuilder AnOrder() { return OrderBuilder.AnOrder(); }}

Page 58: Smidig 2011 TDD Workshop

TESTDATA MED BUILDER PATTERN

[Test]public void Should_generate_shipping_statement_for_order(){ Order order = Build .AnOrder() .ForCustomer(CustomerBuilder.AnCustomer()) .WithLine("Some product", quantity: 1) .WithLine("Some other product", quantity: 2);

var orderProcessing = new OrderProcessing(); orderProcessing.ProcessOrder(order);}

Page 59: Smidig 2011 TDD Workshop

TESTDATA MED BUILDER PATTERN

Don’t repeat yourself

Lesbarhet

Page 60: Smidig 2011 TDD Workshop

CASE: SANTA WISHLIST PROCESSOR ITERASJON 2

• Svært kostbart at alle skal få det de har ønsket seg

• Ønsker å sjekke om barn har vært snille eller ikke. Denne informasjonen ligger i SC(R)UM (Santa Clause Relationship and Uber Management System)

• Integration med SC(R)UM er outsourcet til Finland, så vi skal kun definere grensesnittet mot deres tjenester.

• Kun barn under 18 som har vært snill får pakken sin

Page 61: Smidig 2011 TDD Workshop

CASE: SANTA WISHLIST PROCESSOR ITERASJON 3

• Ønsker å fordele hvilke gaver som skal gis basert på lagerstatus. Skal derfor velge den gaven fra ønskelisten som det er flest av på lager

• Har hatt problemer med å finne adressen. Ønsker derfor å bruke RPS (Rudolf Positioning System) som finner en nøyaktig posisjon for en gitt adresse.

Page 62: Smidig 2011 TDD Workshop

REFERANSE: CLASSIC TDD OR «LONDON SCHOOL»

http://bit.ly/LondonVsClassic

Page 63: Smidig 2011 TDD Workshop

REFERANSE: MOCKS AREN'T STUBS

http://bit.ly/MocksArentStubs

Page 64: Smidig 2011 TDD Workshop

REFERANSE: MOCKING MOCKING AND TESTING OUTCOMES

http://bit.ly/MockingMocking

Page 65: Smidig 2011 TDD Workshop

REFERANSE: INTRODUCING BDD

http://bit.ly/DanBDD

Page 66: Smidig 2011 TDD Workshop

REFERANSE: GROWING OBJECT ORIENTED SOFTWARE GUIDED BY TESTS

http://www.growing-object-oriented-software.com/

Page 67: Smidig 2011 TDD Workshop

TAKK FOR MEG!

Spørsmål?