Unit Testing Succintly Strategier for Unit Tests

Testmetoder er avhengig av hvor du er i prosjektet og budsjettet, når det gjelder tid, penger, arbeidskraft, behov, etc. Ideelt sett er enhetstesting budsjettert i utviklingsprosessen, men realistisk møter vi ofte eksisterende eller eldre programmer som har liten eller ingen kodedekning, men må oppgraderes eller vedlikeholdes. 

Det verste scenariet er et produkt som for tiden blir utviklet, men viser et økt antall feil under utviklingen, igjen med liten eller ingen kodedekning. Som produktleder, enten i begynnelsen av en utviklingsinnsats eller som et resultat av å bli overlevert en eksisterende søknad, er det viktig å utvikle en rimelig enhetsteststrategi. 

Husk at enhetstester skal gi målbare fordeler for prosjektet ditt for å kompensere for ansvaret for utvikling, vedlikehold og egen testing. Videre kan strategien du vedtar for enhetstesting, påvirke arkitekturen i søknaden din. Selv om dette nesten alltid er en god ting, kan det innføre unødvendige kostnader for dine behov.

Begynnelse fra krav

Hvis du starter en tilstrekkelig kompleks applikasjon fra en ren skifer, og alt som er i dine hender, er et sett av krav, bør du vurdere følgende veiledning.

Prioritering av beregningsbehov

Prioritere programmets beregningsbehov for å avgjøre hvor kompleksiteten ligger. Kompleksiteten kan bestemmes ved å oppdage antall stater som en bestemt beregning må imøtekomme, eller det kan være resultatet av et stort sett med inngangsdata som kreves for å utføre beregningen, eller det kan ganske enkelt være algoritmisk komplisert, for eksempel å gjøre feil case analyse på en satellitts redundansering. Vurder også hvor koden sannsynligvis vil endre seg i fremtiden som følge av ukjente endringskrav. Selv om det høres ut som det krever klarsynthet, kan en dyktig programvarearkitekt kategorisere kode i generelle formål (løse et vanlig problem) og domenespesifikke (løse et bestemt kravproblem). Sistnevnte blir en kandidat for fremtidig endring.

Mens du skriver enhetstester for trivielle funksjoner, er det enkelt, raskt og gledelig i antall testtilfeller som programmet slår gjennom, de er de minst kostnadseffektive testene, de tar tid å skrive og fordi de mest sannsynlig vil bli skrevet riktig til å begynne med og de mest sannsynlig vil ikke forandres over tid, de er minst nyttige da programmets kodebase vokser. I stedet fokuserer enhetsteststrategien på koden som er domenespesifikk og kompleks.

Velg en arkitektur

En av fordelene ved å starte et prosjekt fra et sett av krav er at du får opprettet arkitekturen (eller velg en tredjepartsarkitektur) som en del av utviklingsprosessen. Tredjepartsrammer som gir deg mulighet til å utnytte arkitekturer som inversjon av kontroll (og tilhørende konsept av avhengighetsinjeksjon), samt formelle arkitekturer som Model View-Controller (MVC) og Model View ViewModel (MVVM). Enhetstesting av den enkle grunnen at en modulær arkitektur er typisk lettere å måle testen. Disse arkitekturene skiller seg ut:

  • Presentasjonen (visning).
  • Modellen (ansvarlig for utholdenhet og datarrepresentasjon).
  • Kontrolleren (hvor beregningene skal forekomme).

Selv om enkelte aspekter av modellen kan være kandidater for enhetstesting, vil de fleste av enhetstester trolig bli skrevet mot metoder i kontrolleren eller visningsmodellen, hvor beregningene på modellen eller visningen er implementert.

Vedlikeholdsfase

Enhetstesting kan være til nytte selv om du er involvert i vedlikehold av et program, en som enten krever at du legger til nye funksjoner i et eksisterende program eller bare retter på feil av en eldre applikasjon. Det finnes flere tilnærminger man kan ta til en eksisterende søknad og spørsmål som ligger bak disse tilnærmingene som kan bestemme kostnadseffektiviteten til enhetstesting:

  • Skriver du enhetstester bare for nye funksjoner og feilrettinger? Er funksjonen eller feilrettingen noe som vil være til nytte av regresjonstesting, eller er det et engangs-isolert problem som er lettere testet under integreringstesting??
  • Begynner du å skrive enhetstester mot eksisterende funksjoner? Hvis ja, hvordan prioriterer du hvilke funksjoner som skal testes først?
  • Fungerer eksisterende kodebase godt med enhetstesting, eller trenger koden først refactoring for å isolere kodeenheter?
  • Hvilke oppsett eller teardowns er nødvendig for funksjonen eller feil testing?
  • Hvilke avhengigheter kan bli oppdaget om kodendringene som kan føre til bivirkninger i annen kode, og skal enhetstestene bli utvidet for å teste adferd av avhengig kode?

Å gå inn i vedlikeholdsfasen av en eldre applikasjon som mangler enhetstesting, er ikke trivial - planlegging, vurdering og undersøkelse av koden kan ofte kreve flere ressurser enn bare å fikse feilen. Den dårlige bruken av enhetstesting kan imidlertid være kostnadseffektiv, og selv om dette ikke alltid er lett å bestemme, er det verdt øvelsen, hvis det ikke er noe annet enn å få en dypere forståelse av kodebasen.


Bestem prosessen din

Det er tre strategier man kan ta med hensyn til enhetstestprosessen: "Test-Driven Development", "Code First" og, selv om det kan virke antitetisk mot temaet i denne boken, "No Unit Test" -prosessen.

Testdrevet utvikling

En leir er "Testdrevet utvikling", oppsummert av følgende arbeidsflyt:

Gitt et beregningsbehov (se tidligere avsnitt), skriv først en stub for metoden.

  • Hvis avhengigheter på andre objekter som ennå ikke er implementert, kreves (objekter som sendes inn som parametre til metoden eller returnert av metoden), implementere dem som tomme grensesnitt.
  • Hvis egenskaper mangler, implementer stubber for egenskaper som trengs for å bekrefte resultatene.
  • Skriv eventuelle krav til oppsett eller nedrivningstest.
  • Skriv testene. Årsakene til å skrive noen stubber før skriver testen er: først, å utnytte IntelliSense når du skriver testen; For det andre, å fastslå at koden fortsatt samler; og for det tredje, for å sikre at metoden blir testet, har parametrene, grensesnittene og egenskapene synkronisert med hensyn til navngivning.
  • Kjør testene, bekreft at de feiler.
  • Kode gjennomføringen.
  • Kjør testene, bekrefter at de lykkes.

I praksis er dette vanskeligere enn det ser ut. Det er lett å falle bytte for å skrive tester som ikke er kostnadseffektive, og ofte oppdager man at metoden som testes ikke er en tilstrekkelig finkornet enhet til faktisk å være en god kandidat til en test. Kanskje metoden gjør for mye, krever for mye oppsett eller teardown, eller har avhengighet på for mange andre objekter som alle må initialiseres til en kjent tilstand. Dette er alle ting som lettere oppdages når du skriver koden, ikke testen.

En fordel for en testdrevet tilnærming er at prosessen innfører disiplinen av enhetstesting og skriver enhetstester først. Det er enkelt å avgjøre om utvikleren følger prosessen. Med praksis kan man bli lett til å gjøre prosessen kostnadseffektiv.

En annen fordel ved en testdrevet tilnærming er at den i sin natur styrker en slags arkitektur. Det ville være absurd, men mulig å skrive en enhetstest som initialiserer et skjema, setter verdier i en kontroll, og kaller deretter en metode som forventes å utføre noen beregning på verdiene, som denne koden ville kreve (faktisk funnet her):

privat ugyldig btnCalculate_Click (objekt sender, System.EventArgs e) double Principal, AnnualRate, InterestEarned; dobbelt FutureValue, RatePerPeriod; int NumberOfPeriods, CompoundType; Principal = Double.Parse (txtPrincipal.Text); AnnualRate = Double.Parse (txtInterest.Text) / 100; hvis (rdoMonthly.Checked) CompoundType = 12; ellers hvis (rdoQuarterly.Checked) CompoundType = 4; ellers hvis (rdoSemiannually.Checked) CompoundType = 2; ellers CompoundType = 1; NumberOfPeriods = Int32.Parse (txtPeriods.Text); dobbelt i = AnnualRate / CompoundType; int n = CompoundType * NumberOfPeriods; RatePerPeriod = AnnualRate / NumberOfPeriods; FutureValue = Principal * Math.Pow (1 + i, n); InterestEarned = FutureValue - Principal; txtInterestEarned.Text = InterestEarned.ToString ("C"); txtAmountEarned.Text = FutureValue.ToString ("C"); 

Den forrige koden er uprøvbar da den er innfestet med hendelseshåndtereren og brukergrensesnittet. Snarere kan man skrive beregningsmetoden for sammensatte renter:

offentlig enhet CompoundType årlig = 1, halvårlig = 2, kvartalsvis = 4, månedlig = 12 privat dobbelt CompoundInterestCalculation (double principal, double annualRate, CompoundType compoundType, int perioder) double annualRateDecimal = annualRate / 100.0; double i = annualRateDecimal / (int) compoundType; int n = (int) compoundType * perioder; double ratePerPeriod = annualRateDecimal / periods; dobbel futureValue = principal * Math.Pow (1 + i, n); double interestEaned = futureValue - rektor; returnere interesseEaned; 

som vil da tillate en enkel test som skal skrives:

[TestMethod] public void CompoundInterestTest () dobbelt interesse = CompoundInterestCalculation (2500, 7.55, CompoundType.Monthly, 4); Assert.AreEqual (878,21, interesse, 0,01); 

Videre, ved å bruke parameterisert testing, ville det være greit å teste hver sammensatt type, en rekke år og forskjellige renter og hovedstoler.

Den testdrevne tilnærmingen faktisk forenkler en mer formalisert utviklingsprosess ved oppdagelsen av faktiske testbare enheter og isolerer dem fra grenseoverskridende avhengigheter.

Kode først, test andre

Koding først er mer naturlig hvis bare fordi det er vanlig måte applikasjoner er utviklet. Kravet og gjennomføringen kan også virke lett nok ved første øyekast, slik at skrive flere enhetstester virker som en dårlig bruk av tid. Andre faktorer som tidsfrister kan tvinge et prosjekt til en "bare få koden skrevet slik at vi kan sende" utviklingsprosessen.

Problemet med kode-første tilnærming er at det er lett å skrive kode som krever den typen test vi så tidligere. Kode først krever en aktiv disiplin for å teste koden som er skrevet. Denne disiplinen er utrolig vanskelig å oppnå, spesielt siden det alltid er neste nye funksjon å implementere.

Det krever også intelligens, hvis du vil, for å unngå å skrive innklemt, grenseovergangskode og disiplinen til å gjøre det. Hvem har ikke klikket på en knapp i Visual Studio-designeren og kodet hendelsens beregning rett der i stuben som Visual Studio lager for deg? Det er enkelt, og fordi verktøyet styrer deg i den retningen, vil den naive programmereren tro at dette er den riktige måten å kode på.

Denne tilnærmingen krever nøye vurdering av ferdighetene og disiplinen til teamet ditt, og krever nærmere overvåking av teamet, spesielt under høyspenningsperioder når disiplinerte tilnærminger har en tendens til å bryte ned. Tildelt, en testdrevet disiplin kan også kastes ut som tidsfrister, men det pleier å være en bevisst beslutning om å gjøre et unntak, mens det lett kan bli regelen i en kodeksens første tilnærming.

Ingen enhetstester

Bare fordi du ikke har enhetstester betyr det ikke at du taster ut testingen. Det kan ganske enkelt være at testingen understreker godkjenningstestprosedyrer eller integreringstesting.

Balanserende teststrategier

En kostnadseffektiv enhetstestprosess krever en balanse mellom Test-Driven Development, Code First, Test Second og "Test Some Other Way" -strategier. Kostnadseffektiviteten til enhetstesting bør alltid vurderes, så vel som faktorer som utviklerens erfaring med teamet. Som leder kan det hende du ikke vil høre at en testdrevet tilnærming er en god ide om teamet ditt er ganske grønt, og du trenger prosessen for å innføre disiplin og tilnærming.