Test Code Coverage Van mythe naar realiteit

Er was een tijd dat programmeurs werden betaald door het aantal regels code die ze schreven. Ze werden behandeld als broncode producerende machines die in hokjes werkten en in ruil daarvoor dachten ze erover om gewoon een klus te programmeren die ze acht uur per dag doen en dan de rest van de dag te vergeten.

Maar de tijden zijn veranderd. De meeste werkplekken in de cellen verdwenen en programmeurs begonnen hun vak lief te hebben. Met de komst van Agile-technieken en de Software Craftsmanship-beweging zijn veel nieuwe tools naar voren gekomen om de programmeur en het proces te helpen. TDD wordt langzaamaan de de facto manier om code te schrijven en de geheimen van SCRUM of Kanban werden onthuld, zelfs voor de programmeurs in de donkerste hoeken van de kastwereld.

Geautomatiseerde tests en testgestuurde ontwikkeling (TDD) zijn enkele van de essentiële technieken die Agile ons heeft geleverd voor programmeurs. En een tool die met deze methodologieën wordt meegeleverd, wordt gebruikt om testcode-informatie te produceren, wat het onderwerp van dit artikel is.

Definitie

"In de computerwetenschap is codedekking een maat voor de mate waarin de broncode van een programma door een bepaald testsuite wordt getest." ~ Wikipedia

De bovenstaande definitie, overgenomen van Wikipedia, is een van de eenvoudigste manieren om te beschrijven welke codedekking betekent. Kortom, in uw project heeft u een heleboel productiecode en een heleboel testcode. De testcode gebruikt de productiecode en de testdekking vertelt u hoeveel van uw productiecode door de tests werd gebruikt.

Informatie kan op verschillende manieren worden gepresenteerd, van eenvoudige percentages tot mooie afbeeldingen of zelfs realtime markering in uw favoriete IDE.

Laten we het in actie controleren

We gebruiken PHP als de taal om onze code te illustreren. Daarnaast hebben we PHPUnit en XDebug nodig om onze code te testen en dekkingsgegevens te verzamelen.

De broncode

Hier is de broncode die we zullen gebruiken. Je kunt het ook vinden in het bijgevoegde archief.

class WordWrap public function wrap ($ string = ", $ cols) $ string = trim ($ string); if (strlen ($ string)> $ cols) $ lastSpaceIndex = strrpos (substr ($ string, 0, $ cols), "); if ($ lastSpaceIndex! == false && substr ($ string, $ cols, 1)! = ") return substr ($ string, 0, $ lastSpaceIndex)." \ n ". $ this-> wrap (substr ($ string, $ lastSpaceIndex), $ cols); else return substr ($ string, 0, $ cols). "\ n". $ this-> wrap (substr ($ string, $ cols), $ cols);  return $ string;

De bovenstaande code bevat een eenvoudige functie die tekst naar een opgegeven aantal tekens per regel omloopt.

De testcode

We hebben deze code geschreven met behulp van Test Driven Development (TDD) en we hebben er 100% codering voor. Dit betekent dat door onze test uit te voeren, we elke regel van de broncode gebruiken.

require_once __DIR__. '/ ... /WoordWrap.php'; klasse WordWrapTest breidt PHPUnit_Framework_TestCase uit function 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 ('a bc d', 3));

De tests uitvoeren in CLI met tekstdekking

Een manier om dekkingsgegevens te verkrijgen, is door onze tests uit te voeren in de CLI (opdrachtregelinterface) en de uitvoer te analyseren. Voor dit voorbeeld gaan we uit van een UNIX-achtig besturingssysteem (Linux, MacOS, FreeBSD, enz.). Windows-gebruikers moeten de paden en uitvoerbare namen enigszins aanpassen, maar het moet redelijk op elkaar lijken.

Laten we een console openen en mappen veranderen in jouw test map. Ren dan PHPUnit met een optie om dekkingsgegevens te genereren als platte tekst.

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

Dit zou op de meeste systemen uit de doos moeten werken als XDebug is geïnstalleerd, maar in sommige gevallen kunt u een fout tegenkomen die gerelateerd is aan tijdzones.

PHP Waarschuwing: date (): Het is niet veilig om te vertrouwen op de tijdzone-instellingen van het systeem. U bent * verplicht * om de datum.tijdzone-instelling of de functie date_default_timezone_set () te gebruiken. Als u een van deze methoden heeft gebruikt en deze waarschuwing nog steeds wordt weergegeven, heeft u waarschijnlijk de identificatie van de tijdzone verkeerd gespeld. We hebben voorlopig de tijdzone 'UTC' geselecteerd, maar stel date.timezone in om uw tijdzone te selecteren. in phar: ///usr/share/php/phpunit/phpunit.phar/ PHP_CodeCoverage-1.2.10 / PHP / CodeCoverage / Report / Text.php op regel 124 

Dit kan eenvoudig worden opgelost door de voorgestelde instelling in uw php.ini het dossier. U kunt de manier vinden om uw tijdzone in deze lijst op te geven. Ik kom uit Roemenië, dus ik zal de volgende instelling gebruiken:

date.timezone = Europa / Boekarest

Nu, als u de PHPUnit opdracht opnieuw, zou u geen foutenmeldingen moeten zien. In plaats daarvan worden de testresultaten getoond.

PHPUnit 3.7.20 door Sebastian Bergmann ... Tijd: 0 seconden, Geheugen: 5.00Mb OK (2 tests, 7 waarderingen) 

En de dekkingsgegevens bevinden zich in het opgegeven tekstbestand.

$ cat ./coverage.txt Code Dekkingsrapport 2014-03-02 13:48:11 Samenvatting: Klassen: 100,00% (1/1) Methoden: 100,00% (1/1) Regels: 2,68% (14/522) WordWrap Methoden: 100,00% (1/1) Regels: 100,00% (7/7) 

Laten we dit een beetje analyseren.

  • Klassen: verwijst naar hoeveel klassen werden getest en hoeveel daarvan werden behandeld. WordWrap is onze enige klas.
  • methoden: hetzelfde als met klassen. We hebben alleen onze wikkelen() methode, niets anders.
  • lijnen: hetzelfde als hierboven, maar voor regels met code. Hier hebben we veel regels omdat de samenvatting alle regels van PHPUnit zelf bevat.
  • Dan hebben we een sectie voor elke klas. In ons geval is dat alleen WordWrap. Elke sectie heeft zijn eigen methoden en lijndetails.

Op basis van deze waarnemingen kunnen we concluderen dat onze code voor 100% wordt afgedekt door tests. Precies zoals we hadden verwacht voor het analyseren van de dekkingsgegevens.

HTML Coverage Output genereren

Door gewoon een eenvoudige parameter voor PHPUnit te veranderen, kunnen we mooie HTML-uitvoer genereren.

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

Als je je controleert ./ dekking map, daar vind je veel bestanden. Ik zal de lijst hier niet plakken, omdat deze vrij uitgebreid is. In plaats daarvan zal ik je laten zien hoe het eruit ziet in een webbrowser.

Dit is het equivalent van de samenvatting van de bovenstaande tekstversie. We kunnen inzoomen door de voorgestelde links te volgen en meer details te bekijken.

Dekking binnenin onze IO

De voorgaande voorbeelden waren interessant en ze zijn best nuttig, als je code is gebouwd op een externe server waartoe je alleen SHH- of webtoegang hebt. Maar zou het niet leuk zijn om al deze info te hebben, leef in uw IDE?

Als u PHPStorm gebruikt, is alles binnen de afstand van een enkele klik! Selecteer om uw tests met dekking uit te voeren en alle info zal eenvoudigweg verschijnen, magisch.

De informatie over de dekking zal op verschillende manieren en op verschillende plaatsen in uw IDE aanwezig zijn:

  1. Het dekkingspercentage van de test wordt weergegeven in de buurt van elke map en elk bestand.
  2. In de editor markeert elke regel tijdens het bewerken van code links van de regelnummers een groene of rode rechthoek. Groen staat voor geteste lijnen, rood staat voor ongeteste lijnen. Regels zonder code (lege regels, alleen accolades of haakjes, class- of methodeaangiften) hebben geen tekens.
  3. Aan de rechterkant zullen er bestandsbrowsers zijn waar je snel kunt bladeren en bestanden kunt sorteren op dekking.
  4. In de testuitvoer ziet u een tekstregel die u aankondigt dat codedekking is gegenereerd.

De mythen over codedekking

Met zo'n krachtig hulpmiddel in de handen van de ontwikkelaar en onder de neus van het management, was het voor sommige mythen onvermijdelijk om naar boven te komen. Nadat programmeurs weigerden te worden betaald door het aantal coderegels dat ze schrijven, of managers zich realiseerden hoe gemakkelijk het is om het systeem te spelen, begonnen sommigen programmeurs te betalen met het percentage codedekking. Een hogere codedekking betekent dat de programmeur voorzichtiger was, toch? Het is een mythe. Codedekking is geen maatstaf voor hoe goed je code schrijft.

Soms denken programmeurs dat die code met 100% dekking geen fouten bevat. Nog een mythe. Codedekking vertelt u alleen dat u elke regel code hebt getest. Het is een maat voor het aantal uitgeoefende lijnen. Het is geen maatstaf voor het aantal correct geïmplementeerde regels. Half geschreven algoritmen met slechts half gedefinieerde tests hebben bijvoorbeeld nog altijd een dekking van 100%. Dit betekent niet dat het algoritme is voltooid of dat het correct werkt.

Eindelijk, gamen is het systeem erg gemakkelijk. Natuurlijk, als je TDD gebruikt, heb je natuurlijk een hoge dekkingswaarde. Bij hele projecten is 100% onmogelijk. Maar op kleine modules of klassen is het verkrijgen van 100% dekking erg gemakkelijk. Neem bijvoorbeeld onze broncode en stel je voor dat je helemaal geen testen hebt. Wat zou de eenvoudigste test zijn om alle code uit te voeren?

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

Dat is het. Twee beweringen en volledige dekking. Dit is niet wat we willen. Deze test is zo ver van beschrijvend en volledig dat het belachelijk is.

De werkelijkheid over codedekking

Codedekking is een statusindicator, geen eenheid om de prestaties of juistheid te meten.

Codedekking is voor programmeurs, niet voor managers. Het is een manier om problemen in onze code op te sporen. Een manier om oude, ongeteste klassen te vinden. Een manier om paden te vinden die niet door de tests worden gebruikt en die tot problemen kunnen leiden.

Bij echte projecten is de codedekking altijd minder dan 100%. Het bereiken van een perfecte dekking is niet mogelijk, of als dat zo is, is het zelden een vereiste. Als u echter 98% van de dekking wilt hebben, moet u 100% targeten. Als je iets anders als je doelwit hebt, is het zinloos.

Hier is de codedekking op de StorageOS-configuratietoepassing van Syneto.

Het totaal is slechts ongeveer 35%, maar de resultaten moeten worden geïnterpreteerd. De meeste modules bevinden zich in het groen, met een dekking van meer dan 70%. Er is echter een enkele map, vmware, die het gemiddelde naar beneden haalt. Het is een module met veel klassen die alleen definities voor de communicatie-API bevatten. Er is geen reden om die lessen te testen. Ze werden automatisch gegenereerd door vertrouwde code. De programmeurs weten dit en zij zullen weten hoe ze de resultaten moeten interpreteren. Een manager kan erop staan ​​het te testen omdat het een rode balk is en het ziet er verdacht uit voor iemand die de interne details van het project niet kent. Zou het zinvol zijn om het te testen? Helemaal niet! Het zou een nutteloze test zijn, die kostbare tientallen seconden aan bouwtijd kost zonder enig voordeel.

Laatste gedachten

Dus hier is waar we zijn met codedekking: het is een geweldig hulpmiddel voor programmeurs, een bron van informatie om mogelijke problemen te benadrukken, een verkeerd begrepen realiteit voor de meeste managers, en een andere tool om programmeursactiviteiten te dwingen en meten. Zoals met elk ander hulpmiddel, is het er één die gemakkelijk kan worden gebruikt en gemakkelijk kan worden misbruikt.