Testkodstäckning från myt till verklighet

Det var en tid då programmerare betalades av antalet rader kod som de skrev. De behandlades som källkodsproducerande maskiner som arbetar i skåp och i gengäld ansåg de programmering bara ett jobb som de gör åtta timmar om dagen och sedan glömmer det, resten av dagen.

Men tiderna har förändrats. De flesta skåp arbetsplatser försvann och programmerare började älska sina hantverk. Med tillkomsten av Agile-tekniker och Software Craftsmanship-rörelsen framkom många nya verktyg för att hjälpa programmeraren och processen. TDD blir långsamt de facto sättet att skriva kod och hemligheterna i SCRUM eller Kanban avslöts även till programmerarna i de mörkaste hörnen av båsvärlden.

Automatiserad testning och testdriven utveckling (TDD) är några av de väsentliga teknikerna Agile som tillhandahålls till oss programmerare. Och ett verktyg som följer med dessa metoder används för att producera testkodstäckning, vilket är ämnet för denna artikel.

Definition

"I datavetenskap är koddekning ett mått som används för att beskriva i vilken grad källkoden för ett program testas av en viss testpaket." ~ Wikipedia

Definitionen ovan, taget från Wikipedia, är ett av de enklaste sätten att beskriva vilken koddekning som betyder. I själva verket har du i ditt projekt en massa produktionskoder samt en massa testkod. Testkoden utövar produktionskoden och testet täcker dig hur mycket av din produktionskod som utövades av testen.

Information kan presenteras på olika sätt, från enkla procentsatser till bra grafik eller till och med realtidsbelysning i din favorit IDE.

Låt oss kontrollera det i åtgärd

Vi använder PHP som språk för att exemplifiera vår kod. Dessutom behöver vi PHPUnit och XDebug för att testa vår kod och samla täckningsdata.

Källkoden

Här är källkoden vi ska använda. Du kan också hitta den i bifogade arkivet.

$ string = trim ($ string); if (strlen ($ string)> $ cols) $ lastSpaceIndex = strrpos (substr ($ sträng, 0, $ cols), "); om $ lastSpaceIndex! == false && substr ($ string, $ cols, 1)! = ") returnera substr ($ string, 0, $ lastSpaceIndex). sträng, $ lastSpaceIndex), $ cols); annars return substr ($ sträng, 0, $ cols). "\ n". $ this-> wrap (substr ($ sträng, $ kols), $ cols);  returnera $ string;

Ovanstående kod innehåller en enkel funktion som omsluter text till ett visst antal tecken, per rad.

Testkoden

Vi skrev denna kod med hjälp av testdriven utveckling (TDD) och vi har 100% koddekning för den. Det innebär att vi genom att köra vårt test utövar varje rad i källkoden.

require_once __DIR__. '/ ... /WordWrap.php'; klass WordWrapTest utökar PHPUnit_Framework_TestCase funktion testItCanWrap () $ w = new WordWrap (); $ this-> assertEquals (", $ w-> wrap (null, 0)); $ this-> assertEquals (", $ w-> wrap (", 0)); $ this-> assertEquals ('a' $ w-> wrap ('a', 1)); $ this-> assertEquals ("a \ nb", $ w-> wrap ('a b', 1)); $ this-> assertEquals ("ab \ nc ", $ w-> wrap ('ab c', 3)); $ this-> assertEquals (" a \ nbc \ nd ", $ w-> wrap ('en bc d', 3));

Kör testen i CLI med Text Only Coverage

Ett sätt att få täckningsdata är att köra våra test i CLI (kommandoradsgränssnittet) och analysera utmatningen. För detta exempel antar vi ett UNIX-liknande operativsystem (Linux, MacOS, FreeBSD, etc). Windows-användare behöver lite anpassa banor och körbara namn, men det borde vara ganska likartat.

Låt oss öppna en konsol och byta kataloger till din testa mapp. Kör sedan PHPUnit med ett alternativ att generera täckningsdata som vanlig text.

phpunit --coverage-text =. / coverage.txt ./WordWrapTest.php

Detta bör fungera i rutan på de flesta system om XDebug är installerat, men i vissa fall kan du stöta på ett fel relaterat till tidszoner.

PHP Varning: datum (): Det är inte säkert att förlita sig på systemets tidszon inställningar. Du är * obligatorisk * för att använda inställningen date.timezone eller date_default_timezone_set (). Om du använde någon av dessa metoder och du fortfarande får denna varning, har du sannolikt felstavat tidszonidentifieraren. Vi har valt tidszonen 'UTC' för nu, men ange datum.timezone för att välja din tidszon. i phar: ///usr/share/php/phpunit/phpunit.phar/ PHP_CodeCoverage-1.2.10 / PHP / CodeCoverage / Report / Text.php på rad 124 

Det här kan enkelt lösas genom att ange den föreslagna inställningen i din php.ini fil. Du hittar vägen för att ange din tidszon i den här listan. Jag kommer från Rumänien, så jag använder följande inställning:

date.timezone = Europa / Bukarest

Nu, om du kör PHPUnit Kommando igen, du borde inte se några felmeddelanden. Istället kommer testresultaten att visas.

PHPUnit 3.7.20 av Sebastian Bergmann ... Tid: 0 sekunder, Minne: 5,00Mb OK (2 test, 7 påståenden) 

Och täckningsdata kommer att finnas i den angivna textfilen.

$ cat ./coverage.txt Kod Täckningsrapport 2014-03-02 13:48:11 Sammanfattning: Klasser: 100,00% (1/1) Metoder: 100,00% (1/1) Linjer: 2,68% (14/522) WordWrap Metoder: 100,00% (1/1) Linjer: 100,00% (7/7) 

Låt oss analysera det här lite.

  • Klasser: hänvisar till hur många klasser som testades och hur många av dem var täckta. wordwrap är vår enda klass.
  • metoder: samma som med klasser. Vi har bara vår slå in() metod, inget annat.
  • Rader: samma som ovan, men för koder. Här har vi många linjer eftersom sammanfattningen innehåller alla rader från PHPUnit själv.
  • Sedan har vi en sektion för varje klass. I vårt fall är det bara wordwrap. Varje sektion har sina egna metoder och linjedetaljer.

Baserat på dessa observationer kan vi dra slutsatsen att vår kod är 100% täckt av test. Exakt som vi förväntade oss innan vi analyserade täckningsdata.

Generera HTML-täckningsutgång

Genom att bara ändra en enkel parameter för PHPUnit kan vi generera bra HTML-utdata.

$ mkdir ./coverage $ phpunit --coverage-html ./coverage ./WordWrapTest.php 

Om du kontrollerar din ./rapportering katalog hittar du många filer där. Jag kommer inte klistra in listan här eftersom den är ganska omfattande. I stället ska jag visa dig hur det ser ut i en webbläsare.

Detta motsvarar sammanfattningsavsnittet från textversionen ovan. Vi kan zooma in genom att följa de föreslagna länkarna och se mer information.

Täckning Inne i vårt IDE

De tidigare exemplen var intressanta och de är ganska användbara, om Din kod är uppbyggd på en fjärrserver där du bara har SHH eller webbåtkomst till. Men skulle det inte vara trevligt att ha all denna information, bor i din IDE?

Om du använder PHPStorm är allt inom ett enda klick! Välj att köra dina test med täckning och all information kommer helt enkelt att dyka upp, magiskt.

Däckinformationen kommer att finnas i din IDE, på flera sätt och på flera ställen:

  1. Testdekningsprocenten visas i närheten av varje katalog och fil.
  2. I redigeraren, under redigeringskoden, till vänster om linjenumren markeras en grön eller röd rektangel varje rad. Grön representerar testade linjer, rött representerar ej testade. Linjer utan faktisk kod (tomma linjer, endast hakparentesar eller parenteser, klass- eller metoddeklarationer) får inga märken.
  3. På höger sida kommer det att finnas webbläsare där du snabbt kan bläddra och sortera filer genom täckning.
  4. I testutmatningen ser du en rad text som meddelar dig att koddekning genererades.

Myterna om koddekning

Med ett så kraftfullt verktyg i utvecklarens händer och under ledningens näsa var det oundvikligt för vissa myter att yta. Efter att programmerare vägrade betala med antalet kodposter som de skriver, eller ledare insåg hur lätt det är att spela systemet, började några av dem betala programmerare med andelen koddekning. Högre koddekning betyder att programmeraren var försiktig, eller hur? Det är en myt. Kod täckning är inte ett mått på hur bra du skriver kod.

Ibland tenderar programmerare att tro att kod med 100% täckning inte har några fel. En annan myt. Kod täckning berättar bara att du har testat varje rad kod. Det är ett mått på antalet linjer som utövas. Det är inte ett mått på antalet linjer som är korrekt implementerade. Till exempel kommer halvskrivna algoritmer med endast halvdefinierade test fortfarande att ha 100% täckning. Det betyder inte att algoritmen är klar eller att den fungerar korrekt.

Slutligen är spelningssystemet väldigt enkelt. Naturligtvis, om du använder TDD, har du naturligtvis ett högt täckningsvärde. På hela projekt är 100% omöjligt. Men på små moduler eller klasser är det lätt att få 100% täckning. Ta till exempel vår källkod och föreställ dig att du inte har några tester alls. Vad skulle vara det enklaste testet att utöva all kod?

funktion testItCanWrap () $ w = new WordWrap (); $ this-> assertEquals ("a b \ nc", $ w-> wrap ('a b c', 3)); $ this-> assertEquals ("a \ nbc \ nd", $ w-> wrap ('en bc d', 3)); 

Det är allt. Två påståenden och full täckning. Det här är inte vad vi vill ha. Detta test är så långt från beskrivande och komplett att det är löjligt.

Känslomärkningen om koden om verkligheten

Kodtäckning är en statusindikator, inte en enhet som mäter prestanda eller korrekthet.

Kod täckning är för programmerare, inte för chefer. Det är ett sätt att hitta problem i vår kod. Ett sätt att hitta gamla, otestade klasser. Ett sätt att hitta vägar som inte utövas av de tester som kan leda till problem.

På verkliga projekt kommer koddekning alltid att vara under 100%. Att uppnå perfekt täckning är inte möjligt, eller om det är, är det sällan ett måste. Men för att ha 98% av täckningen måste du rikta 100%. Att ha något annat som ditt mål är icke-känsla.

Här är koddäckningen på Synetos StorageOS-konfigurationsprogram.

Totalsumman är endast ca 35%, men resultaten behöver tolkning. De flesta modulerna är i grön, med mer än 70% täckning. Det finns dock en enda mapp, Vmware, vilket drar ner genomsnittet. Det är en modul med många klasser som endast innehåller definitioner för kommunikations API. Det finns ingen anledning att testa de här klasserna. De genererades automatiskt av pålitlig kod. Programmerarna kommer att veta detta och de kommer att veta hur man tolkar resultaten. En chef kan insistera på att testa den eftersom den är en röd stapel och det ser misstänkt ut för någon som inte känner till projektets interna detaljer. Skulle det vara meningsfullt att testa det? Inte alls! Det skulle vara ett oanvändbart test, som skulle ta upp dyrbara tiotals sekunder byggtid utan någon fördel.

Slutgiltiga tankar

Så här är där vi har koddekning: det är ett bra verktyg för programmerare, en källa till information för att belysa eventuella problem, en missförstådd verklighet för de flesta chefer och ett annat verktyg för att tvinga och mäta programmerarens aktiviteter. Som med alla andra verktyg är det en som kan användas korrekt och missbrukas enkelt.