SlideShare a Scribd company logo
1 of 67
TDD: FRA FIZZBUZZ TIL
PRAKTISK BRUK I PROSJEKT, OG
HVORDAN JEG SKRIVER TESTER


        90 min workshop




           Smidig 2011
          Jonas Follesø
             14//11
LITT OM MEG




              Jonas Follesø
              Scientist BEKK Trondheim
              Aktiv i .NET fagmiljøet
              Brukt TDD siden 2006
LITT OM DERE?
AGENDA



         Essensen i TDD
         Feedback Loops
         Lesbare tester
         Mocking
         Builder Pattern
         Satna Wish List Processor
RØD, GRØNN, REFAKTORER
FEEDBACK LOOP I TDD
TDD GIR OSS TILBAMELEDINGER PÅ




                                                        Design
                                 Implementasjon
                                                      (er koden
                                  (virker koden)
                                                   vellstrukturert)
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(){}
}
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
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
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()
{
}

}
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;
}
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");
}
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");
}
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));
    }
}
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));
    }
}
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");
}
ET EKSTRA STEG I TDD-PROSESSEN
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: False
TempTest.cs(35,0): at
SantaWorkshop.Tests.TempTest.Non_readable_diagnostics()

0 passed, 1 failed, 0 skipped, took 0,55 seconds (NUnit 2.5.5).
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).
ET EKSTRA STEG I TDD-PROSESSEN
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
TESTENE BLIR LEVENDE DOKUMENTASJON
LESBARHET




Testkode beskriver hva koden gjør.        Produksjonskode er absrrakt i
Konkret i verdiene som brukes som         verdiene som brukes, men konkret i
eksempler på hva koden gjør, og til å     hvordan den får jobben gjort.
verifisere at den faktisk gjør det, men
abstrakt i hvordan koden fungerer
HVOR BEGYNNER VI?




  TEST ET
  «VANDRENDE SKJELETT»
  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
AKSEPTANSETEST SOM YTRE FEEDBACK LOOP
ATDD I ET STØRRE PERSPEKTIV
UNIT, INTEGRATION &
ACCEPTANCE TEST




         Unit Test              Integration Test                  Acceptance Test




•   Tester vår kode      •   Tester hvordan vår kode      •   Tester systemet ende-til-
                             integrerer med kode vi           ende
•   Uavhengig av miljø
                             ikke kontrollerer
                                                          •   Fokus på brukerhistorier
•   Kjører raskt
                         •   Kan røre miljø (filsystem,
                                                          •   Bruker så realistisk miljø
                             meldingskø)
                                                              som mulig
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
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)
KLASSISK TDD VS
«LONON SCHOOL»-TDD




         Verifisering av   Verifisering av
           tilstand         oppførsel
KLASSISK TDD



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

En algoritme oppdages en test av gangen.
Tester tilstanden til systemet etter
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
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å
«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
ET NETTVERK AV OBJEKTER
HVOR VI ØNSKER Å TESTE ETT OBJEKT FOR SEG
HVOR VI ØNSKER Å TESTE ETT OBJEKT FOR SEG
TESTING MED MOCK OBJEKTER




                            Verifisering av
                             oppførsel
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 == "jonas@follesoe.no")))
                 .Returns(true)
                 .Verifiable("Did not send e-mail to customer as expected");

     var orderProcessing = new OrderProcessing(emailMock.Object);

     var order = new Order {CustomerEmail = "jonas@follesoe.no"};
     orderProcessing.ProcessOrder(order);

     emailMock.Verify();
}
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 ==
"jonas@follesoe.no"))
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 = "jonas@follesoe.no"};
    orderProcessing.ProcessOrder(order);

    emailMock.SendMessageWasCalled.Should().BeTrue("Should send e-mail to customer");
    emailMock.MessageThatWasSent.To.Should().Be("jonas@follesoe.no");
}
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.
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;
    }
}
MOCK RAMMEVERK ELLER
HÅNDSKREVNE MOCKS?


Mock Rammeverk                         Håndskrevne Mocks
• Mulighet for avansert matching på    • Lett å forstå for «nybegynnere»
  parametere
                                       • Lett å debugge gjennom koden
• Slipper å skrive enge mock klasser

• Presise feilmeldinger på hvilke
  expectations som ikke ble møtt
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
TESTING MED MOCK OBJEKTER




                            Verifisering av
                             oppførsel
KLASSISK TDD VS
«LONON SCHOOL»-TDD




         Verifisering av   Verifisering av
           tilstand         oppførsel
DRIVKREFTER FOR DESIGN




          Seperation of   Higher level of
            concerns       abstractions
PORTS AND ADAPTERS




 Unit Test




Integration
/Acceptance
    Test
ARKITEKTUREN OG DESIGNET SOM VIL VOKSE FRAM




                              Unit Test




                             Integration
                             /Acceptance
                                 Test
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);
}
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
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);
}
BUILDER PATTERN IMPLEMENTASJON

public 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;
    }
}
BUILDER PATTERN IMPLEMENTASJON




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

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


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

    public static OrderBuilder AnOrder()
    {
        return OrderBuilder.AnOrder();
    }
}
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);
}
TESTDATA MED BUILDER PATTERN




                               Don’t repeat
           Lesbarhet
                                yourself
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
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.
REFERANSE: CLASSIC TDD OR «LONDON SCHOOL»



http://bit.ly/LondonVsClassic
REFERANSE: MOCKS AREN'T STUBS



http://bit.ly/MocksArentStubs
REFERANSE: MOCKING MOCKING AND TESTING OUTCOMES



http://bit.ly/MockingMocking
REFERANSE: INTRODUCING BDD



http://bit.ly/DanBDD
REFERANSE: GROWING OBJECT ORIENTED SOFTWARE GUIDED BY TESTS



http://www.growing-object-oriented-software.com/
Spørsmål?




TAKK FOR MEG!

More Related Content

Similar to Smidig 2011 TDD Workshop

Solide systemer med unit of work
Solide systemer med unit of workSolide systemer med unit of work
Solide systemer med unit of workEirik Maus
 
Ikke test Puppet-koden din
Ikke test Puppet-koden dinIkke test Puppet-koden din
Ikke test Puppet-koden dinJan Ivar Beddari
 
Introduksjon til TDD
Introduksjon til TDDIntroduksjon til TDD
Introduksjon til TDDJoachim Løvf
 
Objektorientering og design av kode
Objektorientering og design av kodeObjektorientering og design av kode
Objektorientering og design av kodeRune Sundling
 

Similar to Smidig 2011 TDD Workshop (6)

Solide systemer med unit of work
Solide systemer med unit of workSolide systemer med unit of work
Solide systemer med unit of work
 
Ikke test Puppet-koden din
Ikke test Puppet-koden dinIkke test Puppet-koden din
Ikke test Puppet-koden din
 
Introduksjon til TDD
Introduksjon til TDDIntroduksjon til TDD
Introduksjon til TDD
 
Objektorientering og design av kode
Objektorientering og design av kodeObjektorientering og design av kode
Objektorientering og design av kode
 
AWS på kartet
AWS på kartetAWS på kartet
AWS på kartet
 
Devops eller dø!
Devops eller dø!Devops eller dø!
Devops eller dø!
 

More from Jonas Follesø

Windows Phone 7 lyntale fra Grensesnittet Desember 2010
Windows Phone 7 lyntale fra Grensesnittet Desember 2010Windows Phone 7 lyntale fra Grensesnittet Desember 2010
Windows Phone 7 lyntale fra Grensesnittet Desember 2010Jonas Follesø
 
NNUG Trondheim 30.09.2010 - Windows Phone 7
NNUG Trondheim 30.09.2010 -  Windows Phone 7NNUG Trondheim 30.09.2010 -  Windows Phone 7
NNUG Trondheim 30.09.2010 - Windows Phone 7Jonas Follesø
 
Generating characterization tests for legacy code
Generating characterization tests for legacy codeGenerating characterization tests for legacy code
Generating characterization tests for legacy codeJonas Follesø
 
Get a flying start with Windows Phone 7 - NDC2010
Get a flying start with Windows Phone 7 - NDC2010Get a flying start with Windows Phone 7 - NDC2010
Get a flying start with Windows Phone 7 - NDC2010Jonas Follesø
 
Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)
Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)
Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)Jonas Follesø
 
MVVM Design Pattern NDC2009
MVVM Design Pattern NDC2009MVVM Design Pattern NDC2009
MVVM Design Pattern NDC2009Jonas Follesø
 
Silverlight 2 for Developers - TechEd New Zealand 2008
Silverlight 2 for Developers - TechEd New Zealand 2008Silverlight 2 for Developers - TechEd New Zealand 2008
Silverlight 2 for Developers - TechEd New Zealand 2008Jonas Follesø
 

More from Jonas Follesø (8)

Introduction to F#
Introduction to F#Introduction to F#
Introduction to F#
 
Windows Phone 7 lyntale fra Grensesnittet Desember 2010
Windows Phone 7 lyntale fra Grensesnittet Desember 2010Windows Phone 7 lyntale fra Grensesnittet Desember 2010
Windows Phone 7 lyntale fra Grensesnittet Desember 2010
 
NNUG Trondheim 30.09.2010 - Windows Phone 7
NNUG Trondheim 30.09.2010 -  Windows Phone 7NNUG Trondheim 30.09.2010 -  Windows Phone 7
NNUG Trondheim 30.09.2010 - Windows Phone 7
 
Generating characterization tests for legacy code
Generating characterization tests for legacy codeGenerating characterization tests for legacy code
Generating characterization tests for legacy code
 
Get a flying start with Windows Phone 7 - NDC2010
Get a flying start with Windows Phone 7 - NDC2010Get a flying start with Windows Phone 7 - NDC2010
Get a flying start with Windows Phone 7 - NDC2010
 
Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)
Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)
Smidig brukeropplevelse med skjermbildeprototyper (Smidig2009)
 
MVVM Design Pattern NDC2009
MVVM Design Pattern NDC2009MVVM Design Pattern NDC2009
MVVM Design Pattern NDC2009
 
Silverlight 2 for Developers - TechEd New Zealand 2008
Silverlight 2 for Developers - TechEd New Zealand 2008Silverlight 2 for Developers - TechEd New Zealand 2008
Silverlight 2 for Developers - TechEd New Zealand 2008
 

Smidig 2011 TDD Workshop

  • 1.
  • 2. TDD: FRA FIZZBUZZ TIL PRAKTISK BRUK I PROSJEKT, OG HVORDAN JEG SKRIVER TESTER 90 min workshop Smidig 2011 Jonas Follesø 14//11
  • 3. LITT OM MEG Jonas Follesø Scientist BEKK Trondheim Aktiv i .NET fagmiljøet Brukt TDD siden 2006
  • 5. AGENDA Essensen i TDD Feedback Loops Lesbare tester Mocking Builder Pattern Satna Wish List Processor
  • 7. TDD GIR OSS TILBAMELEDINGER PÅ Design Implementasjon (er koden (virker koden) vellstrukturert)
  • 8. 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(){} }
  • 9. 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
  • 10. 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
  • 11. 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() { } }
  • 12. 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; }
  • 13. 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"); }
  • 14. 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"); }
  • 15. 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)); } }
  • 16. 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)); } }
  • 17. 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"); }
  • 18. ET EKSTRA STEG I TDD-PROSESSEN
  • 19. 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: False TempTest.cs(35,0): at SantaWorkshop.Tests.TempTest.Non_readable_diagnostics() 0 passed, 1 failed, 0 skipped, took 0,55 seconds (NUnit 2.5.5).
  • 20. 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).
  • 21. ET EKSTRA STEG I TDD-PROSESSEN
  • 22. 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
  • 23. TESTENE BLIR LEVENDE DOKUMENTASJON
  • 24. LESBARHET Testkode beskriver hva koden gjør. Produksjonskode er absrrakt i Konkret i verdiene som brukes som verdiene som brukes, men konkret i eksempler på hva koden gjør, og til å hvordan den får jobben gjort. verifisere at den faktisk gjør det, men abstrakt i hvordan koden fungerer
  • 25. HVOR BEGYNNER VI? TEST ET «VANDRENDE SKJELETT» 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
  • 26. AKSEPTANSETEST SOM YTRE FEEDBACK LOOP
  • 27. ATDD I ET STØRRE PERSPEKTIV
  • 28. UNIT, INTEGRATION & ACCEPTANCE TEST Unit Test Integration Test Acceptance Test • Tester vår kode • Tester hvordan vår kode • Tester systemet ende-til- integrerer med kode vi ende • Uavhengig av miljø ikke kontrollerer • Fokus på brukerhistorier • Kjører raskt • Kan røre miljø (filsystem, • Bruker så realistisk miljø meldingskø) som mulig
  • 29. 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
  • 30. 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)
  • 31. KLASSISK TDD VS «LONON SCHOOL»-TDD Verifisering av Verifisering av tilstand oppførsel
  • 32. KLASSISK TDD Kent Beck’s «Test-driven Development By Example» En algoritme oppdages en test av gangen. Tester tilstanden til systemet etter
  • 33. 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
  • 34. 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å
  • 35. «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
  • 36. ET NETTVERK AV OBJEKTER
  • 37. HVOR VI ØNSKER Å TESTE ETT OBJEKT FOR SEG
  • 38. HVOR VI ØNSKER Å TESTE ETT OBJEKT FOR SEG
  • 39. TESTING MED MOCK OBJEKTER Verifisering av oppførsel
  • 40. 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 == "jonas@follesoe.no"))) .Returns(true) .Verifiable("Did not send e-mail to customer as expected"); var orderProcessing = new OrderProcessing(emailMock.Object); var order = new Order {CustomerEmail = "jonas@follesoe.no"}; orderProcessing.ProcessOrder(order); emailMock.Verify(); }
  • 41. 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 == "jonas@follesoe.no"))
  • 42. 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 = "jonas@follesoe.no"}; orderProcessing.ProcessOrder(order); emailMock.SendMessageWasCalled.Should().BeTrue("Should send e-mail to customer"); emailMock.MessageThatWasSent.To.Should().Be("jonas@follesoe.no"); }
  • 43. 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.
  • 44. 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; } }
  • 45. MOCK RAMMEVERK ELLER HÅNDSKREVNE MOCKS? Mock Rammeverk Håndskrevne Mocks • Mulighet for avansert matching på • Lett å forstå for «nybegynnere» parametere • Lett å debugge gjennom koden • Slipper å skrive enge mock klasser • Presise feilmeldinger på hvilke expectations som ikke ble møtt
  • 46. 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
  • 47. TESTING MED MOCK OBJEKTER Verifisering av oppførsel
  • 48. KLASSISK TDD VS «LONON SCHOOL»-TDD Verifisering av Verifisering av tilstand oppførsel
  • 49. DRIVKREFTER FOR DESIGN Seperation of Higher level of concerns abstractions
  • 50. PORTS AND ADAPTERS Unit Test Integration /Acceptance Test
  • 51. ARKITEKTUREN OG DESIGNET SOM VIL VOKSE FRAM Unit Test Integration /Acceptance Test
  • 52. 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); }
  • 53. 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
  • 54. 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); }
  • 55. BUILDER PATTERN IMPLEMENTASJON public 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; } }
  • 56. BUILDER PATTERN IMPLEMENTASJON public CustomerBuilder WithName(string name) { customer.Name = name; return this; } public CustomerBuilder WithAddress(Address address) { customer.Address = address; return this; }
  • 57. BUILDER PATTERN FACTORY public static class Build { public static CustomerBuilder ACustomer() { return CustomerBuilder.ACustomer(); } public static OrderBuilder AnOrder() { return OrderBuilder.AnOrder(); } }
  • 58. 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); }
  • 59. TESTDATA MED BUILDER PATTERN Don’t repeat Lesbarhet yourself
  • 60. 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
  • 61. 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.
  • 62. REFERANSE: CLASSIC TDD OR «LONDON SCHOOL» http://bit.ly/LondonVsClassic
  • 63. REFERANSE: MOCKS AREN'T STUBS http://bit.ly/MocksArentStubs
  • 64. REFERANSE: MOCKING MOCKING AND TESTING OUTCOMES http://bit.ly/MockingMocking
  • 66. REFERANSE: GROWING OBJECT ORIENTED SOFTWARE GUIDED BY TESTS http://www.growing-object-oriented-software.com/

Editor's Notes

  1. A better alternative is to name tests in terms of the features that the target object provides.We use a TestDox convention (invented by Chris Stevenson) where each test name reads like a sentence, with the target class as the implicit subject.