Pokrycie kodu testami i rzut oka na NCrunch-a i NCover-a

Co to jest CodeCoverage?

Jest to pokrycie kolejnych linii kodu testami jednostkowymi. Metryka ta pokazuje ile procent linijek kodu ma przynajmniej jeden test jednostkowy, który ją wykonuje. Sprawa wydaje się prosta, tyle tylko, że w świecie .neta kod kompiluje się do… no właśnie do kodu pośredniego. Dopiero ten kod pośredni jest kompilowany w razie potrzeby do kodu maszynowego. Co więcej o ile nie wprowadzamy zmian do kodu źródłowego, to źródła się nie zmieniają ale już kod kompilowany może się zmieniać pomiędzy kolejnymi kompilacjami (*). Czyli wynika z tego, że przydało by się sprawdzać dwa pokrycia kodu. Jedno dla plików źródłowych a drugie dla kodu wynikowego: Obrazowo:

[csharp]

return IsEmpty ? new SomeObject() : _someVariable;

[/csharp]

to tylko jedna linijka ale w MSIL-u to już ładnych kilka linijek.

[csharp]

// Code size 23 (0x17)
.maxstack 1
.locals init ([0] class ClassLibrary3.SomeObject CS$1$0000)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: brtrue.s IL_000c
IL_0004: ldarg.0
IL_0005: ldfld class ClassLibrary3.SomeObject ClassLibrary3.Class1::_someVariable
IL_000a: br.s IL_0011
IL_000c: newobj instance void ClassLibrary3.SomeObject::.ctor()
IL_0011: nop
IL_0012: stloc.0
IL_0013: br.s IL_0015
IL_0015: ldloc.0
IL_0016: ret
[/csharp]

Dlatego przy pokryciu kodu możemy mówić o symbol coverage – czyli pokrycie symboli, na których (w uproszczeniu) debuger może się zatrzymać – co w większości wypadków oznacza całą linijkę kodu, o branch coverage czyli pokyciu rozgałęzień czyli miejsc, gdzie kod się potencjalnie rozgałęzia (if-y, switch-e, foreach-e i tym podobne) oraz method coverage, czyli już bardzo zgrubny wynik pokazujący ile metod zostało wywołanych. Tyle przydługiego wstępu teraz do programów. NCrunch posiada wbudowany code coverage, który działa (jak na moje oko) jako symbol coverage – zresztą nawet jeśli działał by inaczej to na chwilę obecną nie jest wstanie pokazywać pół kropek w sytuacjach jeśli w linijce jest rozgałęzienie. Jest to bardzo przydany i pomocny dodatek jednak jest niezbyt dokładny – dostajemy przyzwoitą dokładność przy przyzwoitej szybkości. NCover z drugiej strony to narzędzie specjalizujące się w obliczaniu pokrycia kodu testami i podaje nam aż trzy wyniki: symbol, branch  i metod coverage. To zapewnia nam maksymalną możliwą dokładność za cenę dłuższego oczekiwania.

Jak korzystam z tych narzędzi?

NCrunch mam włączony cały czas. Jest jakby takim policjantem, który ciągle pilnuje (a przynajmniej podpowiada bo na razie, na szczęście, nie usuwa automatycznie niewytestowanego kodu). Mam świadomość tego, że wyniki pokrycia kodu mogą być niedokładne jednak tą informację otrzymuję praktycznie natychmiastowo. NCover używam z doskoku. Czyli raz na tydzień czy raz na miesiąc przychodzi pora na tzw. czesanie trawy – czyli wyszukiwanie miejsc niepokrytych testami jednostkowymi. Co ciekawe (albo przerażające) często można najzwyczajniej usunąć taki nieprzetestowany kod i aplikacja działa dalej tak samo 🙂 (chociaż trzeba przyznać, że nie zawsze jest tak różowo).

Wnioski

Korzystając z narzędzia typu Code Coverage, należy się zastanowić jakie wyniki tak naprawdę dostajemy. Te mniej dokładne ale szybsze nadają się do zgrubnego sprawdzania, te bardziej dokładne i wolniejsze do bardziej dogłębnej analizy. Czy to źle? W żadnym wypadku. Należy pamiętać, że to są narzędzia dla programisty(**) i w zależności od potrzeby danej chwili, różne wyniki mogą być zupełnie wystarczające i odpowiednie.

(*) jeśli czytasz tą gwiazdkę to zapewne zastanawiasz się jak to możliwe, że dwa razy skompilowany kod może dawać różne wyniki, jak-to, kiedy. Wyjaśnienie znajdziesz w jutrzejszym wpisie – zapraszam

 (**) o tym też będzie osobny wpis