Przejdź do treści

DevStyle - Strona Główna
[UT-4.1] (Nie)Testowanie metod prywatnych

[UT-4.1] (Nie)Testowanie metod prywatnych

Maciej Aniserowicz

5 grudnia 2011

Backend

[ten post jest częścią mojego minicyklu o testach, pełna lista postów: tutaj]

W komentarzach do ostatniego posta wywiązała się dyskusja na temat “a co z metodami prywatnymi?“. Odpowiedź najkrótsza z możliwych brzmi: NIC. Zainteresowanych odsyłam do tamtejszych wypowiedzi, a w niniejszej notce postaram się zawarte tam myśli rozwinąć.

Zaczynając przygodę z testami jednostkowymi często stawałem przed dylematem “jak mam przetestować funkcjonalność z metod prywatnych?“. Sporo się naszukałem i naczytałem o różnych rozwiązaniach, z czego dwa zdawały się być najpopularniejsze i najbardziej rekomendowane. I teraz, o kilka lat mądrzejszy,  mogę z czystym sumieniem powiedzieć: oba były błędne.

Pierwsza rada to “skorzystaj z refleksji“. Fakt, da się w ten sposób wywołać metodę prywatną, ale czy to przypadkiem nie jest zbytnia ingerencja w sam testowany obiekt? Gdyby metoda miała z założenia być wołana z zewnątrz, to by nie była prywatna! I tu wchodzi druga rada: “upublicznij metodę“. Ale skoro dana metoda była prywatna, to jej upublicznienie tylko i wyłącznie na potrzeby testów zaśmieci nam interfejs klasy. I… śmierdzi.

Tak źle i tak niedobrze.

Moja rada, i rada Zacnych Komentatorów: nie testuj metod prywatnych. Z dwóch powodów:

1) one i tak zostaną “niejawnie” przetestowane poprzez testy metod publicznych…

2) … a jeśli nie, to oznacza że wcale nie są potrzebne, więc nie ma sensu ich testować

Ale ja naprawdę czuję że powinienem bo są skomplikowane!

Jeśli tak… to Twój kod wymaga zmian. Czy funkcjonalność w tej prywatnej metodzie nie jest przypadkiem zbyt przepchanym logiką helperem, który tak naprawdę w ogóle nie powinien znaleźć się w tej klasie? Pamiętaj o Single Responsibility Principle: “every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class“.

Przytoczę przykład, który podałem we wspomnianych komentarzach: mamy klasę posiadającą ID, jednak nie dostaje go wprost. Zamiast tego z jakiegoś zewnętrznego systemu dajemy jej stringa i mówimy “znajdź tu sobie swoje ID“. Z życia wzięte: FIM reprezentuje referencje między obiektami poprzez taki string: “urn:uuid:b116583c-a03e-4ea8-a895-a9511a4714b3“, gdzie pierwsze dwa elementy są stałe, a trzeci to ID obiektu powiązanego.

Sytuacja wymagająca testowania metody prywatnej zakłada, że klasa taka ma zaszytą logikę poszukiwania GUIDa pod deklaracją “private string ExtractId(string source)“.

Ale czy naprawdę klasa reprezentująca jakiś obiekt w systemie powinna się takimi pierdołami zajmować? Najprawdopodobniej NIE! Wyrzućmy ten kod na zewnątrz, do, dajmy na to, FimReferenceIdExtractor. Tam nie będzie to już metodą prywatną – tam będzie to główną odpowiedzialnością klasy. I metoda “ExtractId” nie będzie prywatnym helperem, spychanym na sam dół pliku i chowanym w jakichś regionach jak bękart na królewskim dworze. Będzie po prostu częścią interfejsu! A co za tym idzie: idealnym kandydatem do przetestowania.

Do głowy przychodzą mi też momentalnie moje dawne porażki z testami kończące się stwierdzeniem:

– “moje rozwiązanie jest na tyle nietypowe że nie da się go przetestować

– Dlaczego?

– “Bo nie umiem przetestować obsługi zdarzeń w *.aspx albo Form*.cs

– Po pierwsze: co w tym nietypowego (niestety)? A po drugie: po co chcesz testować obsługę zdarzeń w tych plikach?

– “Bo tam mam najwięcej kodu

– …

No i właśnie. Wtedy siedziałem całymi dniami i drapałem się po łbie kombinując jak to możliwe, że akurat MÓJ kod nie nadaje się do testowania. Dziś powiedziałbym sobie bardzo dosadnie: wyciągnij-że ten kod do klas, które naprawdę powinny go zawierać!

Z dużą dozą prawdopodobieństwa można stwierdzić, że prywatne metody wymagające osobnych testów łamią zasadę SRP i powinny być przeniesione do dedykowanego dla nich miejsca.

Bardzo fajnie ujął to w komentarzu @rek:

Metod prywatnych nie testujemy, jeśli uważasz, że metoda prywatna powinna być otestowana to znaczy że zrobiłeś coś źle…. refactor it….. wyciągnij do osobnej klasy (SRP) wraz z testami

A dobitniej podchwycił Paweł:

Masz cos prywatnego, co musisz zewnetrznie przetestowac -> zabrnales w guwno (moze nawet po pas) -> refaktoryzuj ASAP“.

Może jeszcze ktoś coś doda, czy osiągnęliśmy stan globalnego współdzielenia poglądu, co w tym kraju jest sztuką niemałą?:)

Zobacz również