En praktisk introduktion til testdrevet udvikling

Testdrevet udvikling er hård! Dette er den utallige sandhed om det.

I disse dage læser du masser af artikler om alle fordelene ved at lave Test Driven Development (TDD). Og du hører sandsynligvis mange samtaler på teknologikonferencer, der fortæller dig, at "Gør testene!", Og hvor sejt det er at gøre dem.

Og ved du hvad? Desværre har de ret (ikke nødvendigvis om den “seje” del, men om den nyttige del). Test er et MUST ! De typiske fordele, vi nævner, når det kommer til at tale om TDD, er virkelige:

  • Du skriver bedre software
  • Du har beskyttelse mod at bryde verden, når nye funktioner introduceres
  • Din software er selvdokumenteret
  • Du undgår over-engineering

Selvom jeg altid har været enig i disse fordele, var der en tid, hvor jeg troede, at jeg ikke havde brug for TDD for at skrive god og vedligeholdelig software. Selvfølgelig ved jeg nu, at jeg tog fejl, men hvorfor havde jeg denne idé på trods af proffernes skinnende magi? Årsagen er kun én: og lad mig bede Rihanna om at sige det for mig ...

Prisen!

Det koster meget! Sandsynligvis tænker nogen ” men det koster endnu mere, hvis du ikke laver testene ” - og det er også rigtigt. Men disse to omkostninger kommer på forskellige tidspunkter:

  • du gør TDD ➡ du har en pris nu .
  • Du gør ikke TDD ➡ du vil have en pris i fremtiden .

Så hvordan kommer vi ud af denne blindgyde?

Den mest effektive måde at få noget gjort på er at gøre det så naturligt som muligt. Menneskets natur er at være doven (her er softwareudviklere de bedst performende) og grådige, så du skal finde din måde at reducere omkostningerne på nu .Det er let at sige, men så svært at gøre!

Her vil jeg dele min erfaring og hvad der har fungeret for mig med at vende fordel / omkostningsforholdet til min fordel.

Men inden jeg gør det, lad os analysere nogle typiske vanskeligheder ved at anvende TDD.

Kan du teste summen af ​​to tal?

Generelt er teori ikke valgfri; du er nødt til at mestre det for at mestre praksis. At prøve at anvende på en gang al den teoretiske viden, du tidligere har erhvervet, kan dog have følgende effekt:

Den typiske teoriundervisning om TDD starter med noget som dette:

Og her er du som

Så kommer dette:

  • rød ➡ grøn ➡ refaktorcyklus
  • enhed, accept, regression, integrationstest
  • hån, stubbe, forfalskninger
  • hvis du er heldig (eller måske uheldig?), vil nogen fortælle dig om kontraktprøvning
  • og hvis du er meget heldig (eller måske meget uheldig?) vil du røre ved ældre codebase refactoring

Det bliver svært, men du er en erfaren udvikler, og alle disse koncepter er ikke så svære at håndtere for dig. Derefter slutter klassen; du går hjem, og i løbet af de næste dage laver du flittigt nogle kodekatas for at løse de begreber, du lige har lært. Så langt så godt.

Kampen er reel

Dernæst kommer et virkeligt verdensprojekt med reelle deadlines og reelle timingomkostninger - men du er motiveret til at anvende din skinnende nye TDD. Du begynder at tænke på arkitekturen af ​​din software og begynder at skrive tests til første klasse og selve klassen - lad os kalde det Class1 .

Nu tænker du på den første bruger af Class1, lad os kalde det UsageOfAClass, og igen tester og skriver du det. Class1 er en samarbejdspartner af UsageOfAClass, så vil du spotte det? Ok, lad os spotte det. Men hvad med ægte interaktioner mellem Class1 og UsageOfAClass? Måske skulle du teste dem alle også? Lad os gøre det.

På dette tidspunkt begynder du inden i dig at høre en lille stemme, der siger " Jeg ville udvikle mig meget hurtigere, hvis jeg ikke skulle skrive disse tests ... ". Du lytter ikke til denne onde stemme og fortsætter direkte til næste test.

Class2 vil blive brugt af UsageOfAClass, og den fortsætter sig inde i en Db. Så skal vi teste Class2, dets interaktion med UsageOfAClass og vedholdenheden i Db? Men vent ... nævnte nogen, hvordan man skulle klare I / O-test under TDD-teoriklassen?

Teorien bag TDD er ikke så vanskelig at forstå, men at anvende den på den virkelige verden kan være virkelig kompleks, hvis du ikke nærmer dig den rigtige måde.

Bare gør det

Vi skal altid huske på, at teorien skal bøjes efter vores behov og ikke det modsatte.

Hovedmålet er at få arbejdet gjort. Så mit råd er, bare gør det !

Start simpelt og gør bare din opgave indtil slutningen. Så når du sidder fast i en eller anden teoretisk sindsløjfe som:

  • er dette en enhed eller en integrationstest?
  • her skal jeg spotte det eller ej?
  • åh lort, her skal jeg skrive en ny samarbejdspartner, så en helt ny serie af uendelige enhedstest bare for at skrive "hej, banan" ...

bare glem teorien et stykke tid og tag et skridt fremad. Bare gør det som det kommer!

Når du er færdig med din opgave, skal du se tilbage på dit arbejde. Når jeg ser tilbage på det afsluttede job, vil det være meget lettere at analysere, hvad der ville have været den rigtige ting at gøre.

Praktisk TDD

Bare gør det. Forresten tror jeg, det også er den rigtige tilgang til TDD.

Hvad var der galt i, hvordan vi byggede Class1, Class2 og UsageOfAClass? Tilgangen.

Dette er en bottom-up-tilgang:

  • analysere problemet
  • finde ud af en arkitektur
  • begynd at bygge det fra enhedskomponenter

Denne tilgang er den bedste ven af over-engineering . Du bygger typisk systemet for at forhindre ændringer, som du tror vil komme i fremtiden uden at vide, om de rent faktisk vil. Derefter når nogle krav ændres, sker det typisk på en måde, der ikke passer til din struktur, uanset hvor god den er.

For mig har nøglen til drastisk reduktion af de øjeblikkelige omkostninger ved at skrive med TDD været at tage en top-down tilgang:

  1. bringe en brugerhistorie
  2. skriv en meget enkel test af en brugssag
  3. få det til at køre
  4. gå tilbage til trin 2, indtil alle brugssager er færdige

Mens du gør denne proces, skal du ikke bekymre dig for meget om arkitektur, ren kode (husk i det mindste at bruge anstændige variablernavne) eller enhver form for komplikation, der ikke er nødvendig i øjeblikket. Bare gør hvad du ved, du har brug for nu , indtil slutningen.

Test af historien angiver klart, hvad der er de nuværende og kendte krav.

Når du er færdig, skal du kigge på din store kugle med spaghetti mudderkode, komme over skammen og se dybere på, hvad du har gjort:

  • det virker! Og test viser det.
  • Alt systemet er der, og lige hvad der faktisk er nødvendigt for at få arbejdet gjort .

Nu har du et overblik over alle dele af dit system, så du kan reflektere med den viden om domænet, som du ikke kunne have haft, da du startede fra bunden. Og test vil sikre, at intet går i stykker, mens refactoring.

Refactoring

Den bedste måde for mig at begynde at refakteere er at identificere ansvarsområder og adskille dem i private metoder. Dette trin hjælper med at identificere ansvarsområder og deres input og output.

Derefter er klasser af samarbejdspartnere næsten der, og du skal bare flytte dem til forskellige filer.

Når du fortsætter, skal du først skrive test til de klasser, der kommer ud af processen og gentage, indtil du er tilfreds med resultatet. Og husk, hvis du sidder fast et eller andet sted, skal du bare gøre det! Hvis du gør noget dårligt, vil du, når du er færdig, have flere oplysninger om, hvordan du kommer over fejlen, næste gang du møder den. At få arbejdet gjort er prioriteret efter bedste evne.

På denne måde, hvis du analyserer dine fejl for at lære af dem, vil du også forbedre dine evner.

Den næste brugerhistorie

Fortsæt med at udvikle dit produkt ved at følge disse trin:

  • tage en historie
  • få det til at fungere fuldstændigt i en "testkode" -cyklus.
  • refaktor

Mens du tilføjer funktioner, vil du fortsætte med at ændre din software og måske endda dens struktur. Men når systemet vokser, vil omkostningerne ved ændringer opretholde en lineær vækst takket være de to hovedfunktioner i TDD:

  • arkitektur opdagelse (der hjælper med at kontrollere kompleksiteten)
  • beskyttelse mod at bryde ændringer

Systemet overkonstrueres ikke, da arkitektur kommer til at opstå, når historier bliver afsluttet. Du tænker ikke over, hvad der kan være fremtidige krav; hvis du ender med at have brug for det, vil omkostningerne ved at implementere det være lave.

Hvad kan få det til at gå galt?

Historiens størrelse. Hvad du bygger op til slutningen skal have den rigtige størrelse. Ikke for stor (ellers tager det for meget tid at få feedback) eller for lille (ellers har du ikke oversigten).

Hvad hvis historien er for stor? Opdel det i stykker, der kan bygges fra start til slut.

Hvad er det næste?

I den næste artikel vil jeg give et praktisk eksempel på de begreber, jeg forklarede her. Vi implementerer trin for trin Bowling Game kata startende fra en accept test.

Det er ikke et virkeligt verdensproblem, men det har tilstrækkelig kompleksitet til at se, hvordan TDD kan hjælpe med at håndtere det.

Del din mening og forslag om denne artikel. Er du enig med mig, eller tror du, at alt dette er en masse affald? Lad mig vide, hvad du synes i kommentarer; det ville være meget rart at starte en samtale om TDD og dele vores oplevelser.

Jeg vil gerne takke Matteo Baglini for at hjælpe mig med at finde vej gennem en praktisk tilgang til softwareudvikling og TDD.

Tak fordi du læste!

Forsidebillede med tilladelse til testsigma.