Jak mogę zalecić testowanie jednostkowe kodu prywatnego?

15

Próbuję zalecać testowanie jednostkowe w mojej grupie roboczej, ale często spotykam się z zastrzeżeniem, że powinien on być używany tylko w przypadku eksportowanego z zewnątrz interfejsu API (który jest jedynie minimalną i niekrytyczną częścią naszego systemu), a nie wewnętrznego i prywatnego kod (który ma teraz tylko testy funkcjonalne).

Chociaż uważam, że ten test jednostkowy można i należy zastosować do całego kodu, jak mogę przekonać moich współpracowników?

Wizard79
źródło
3
Jeśli masz prywatne metody, które chcesz przetestować, często oznacza to, że Twój kod narusza SRP i jest tam inna klasa, która prosi, aby ją wyodrębnić i przetestować samodzielnie.
Paddyslacker,
@Paddyslacker: Czuję, że cały kod wymaga przetestowania. Nie rozumiem, dlaczego jednostka kodu zgodna z zasadą pojedynczej odpowiedzialności nie powinna być poddawana testom jednostkowym ...
Wizard79
4
@lorenzo, przegapiłeś mój punkt; może nie zrobiłem tego zbyt dobrze. Jeśli wyodrębnisz te prywatne metody do innej klasy, będą one musiały być dostępne z oryginalnej klasy. Ponieważ metody są teraz publiczne, należy je przetestować. Nie sugerowałem, że nie powinny być testowane, sugerowałem, że jeśli czujesz potrzebę bezpośredniego przetestowania metod, prawdopodobnie nie powinny one być prywatne.
Paddyslacker,
@Paddyslacker: Czuję potrzebę bezpośredniego testowania również metod prywatnych. Jak myślisz, dlaczego nie powinny być prywatne?
Wizard79
6
Testując metody prywatne, przełamujesz abstrakcję. Powinieneś testować stan i / lub zachowanie, a nie implementację, w testach jednostkowych. Twoje przykłady / scenariusze powinny być w stanie zweryfikować, jaki jest wynik prywatnego kodu - jeśli uznasz to za trudne, to jak mówi Paddyslacker, może to oznaczać, że naruszasz SRP. Może to również oznaczać, że nie destylowałeś swoich przykładów, aby naprawdę reprezentować to, co robi Twój kod.
FinnNk,

Odpowiedzi:

9

Twoi współpracownicy mogą mylić prawdziwe testy jednostkowe z testami integracji. Jeśli twój produkt jest (lub ma) interfejs API, testy integracji można zaprogramować jako przypadki testowe NUnit. Niektóre osoby błędnie uważają, że są to testy jednostkowe.

Możesz spróbować przekonać współpracowników w następujący sposób (jestem pewien, że już to znasz, mówię tylko, że wskazanie tego współpracownikom może pomóc):

  • Pokrycie testowe . Zmierz rzeczywisty procent pokrycia testowego tych testów integracyjnych. Jest to sprawdzenie rzeczywistości dla tych, którzy nigdy nie przeprowadzali testów. Ponieważ trudno jest przećwiczyć wszystkie logiczne ścieżki, gdy wejście znajduje się w odległości kilku warstw, zasięg testu osiąga wartość od około 20% do 50%. Aby uzyskać większy zasięg, współpracownicy muszą pisać prawdziwe, izolowane testy jednostkowe.
  • Konfiguracja . Wdróż to samo testowane oprogramowanie, a być może możesz pokazać współpracownikom, jak trudno jest przeprowadzić ich testy w innym środowisku. Ścieżki do różnych plików, parametry połączenia DB, adresy URL usług zdalnych itp. - wszystko się sumuje.
  • Czas wykonania . Jeśli testy nie są prawdziwymi testami jednostkowymi i nie można ich uruchomić w pamięci, uruchomienie ich zajmie dużo czasu.
azheglov
źródło
12

Powody stosowania testów jednostkowych w kodzie wewnętrznym / prywatnym są dokładnie takie same jak w przypadku API obsługiwanych zewnętrznie:

  • Zapobiegają ponownemu pojawianiu się błędów (testy jednostkowe stanowią część zestawu testów regresji).
  • Dokumentują (w formacie wykonywalnym!), Że kod działa.
  • Zapewniają one wykonywalną definicję tego, co oznacza „kod”.
  • Zapewniają one zautomatyzowany sposób wykazania, że ​​kod rzeczywiście odpowiada specyfikacjom (jak zdefiniowano w powyższym punkcie).
  • Pokazują, w jaki sposób jednostka / klasa / moduł / funkcja / metoda zawodzi w przypadku nieoczekiwanego wejścia.
  • Podają przykłady użycia jednostki, która jest świetną dokumentacją dla nowych członków zespołu.
Frank Shearar
źródło
8

Jeśli masz na myśli prywatność tak, jak myślę, masz na myśli, to nie - nie powinieneś testować tego jednostkowo. Powinieneś testować tylko jednostki możliwe do zaobserwowania zachowanie / stan. Być może brakuje Ci punktu za cyklem „refrakcji czerwono-zielonej” TDD (a jeśli nie przeprowadzasz testu najpierw, obowiązuje ta sama zasada). Po napisaniu i zdaniu testów nie chcesz, aby się zmieniały podczas refaktoryzacji. Jeśli jesteś zmuszony do testowania jednostkowego funkcjonalności prywatnych, prawdopodobnie oznacza to, że testy jednostkowe wokół funkcjonalności publicznej są wadliwe. Jeśli pisanie testów wokół kodu publicznego jest trudne i skomplikowane, być może twoja klasa robi za dużo lub twój problem nie jest jasno określony.

Co gorsza, z czasem twoje testy jednostkowe staną się kulą i łańcuchem spowalniając cię bez dodawania jakiejkolwiek wartości (zmiana implementacji, na przykład optymalizacja lub usunięcie duplikacji, nie powinna mieć wpływu na testy jednostkowe). Kod wewnętrzny powinien jednak być testowany jednostkowo, ponieważ zachowanie / stan jest obserwowalny (tylko w ograniczony sposób).

Kiedy po raz pierwszy przeprowadziłem testy jednostkowe, podciągnąłem różne sztuczki, aby przetestować prywatne rzeczy, ale teraz, mając kilka lat za sobą, uważam to za gorsze niż strata czasu.

Oto trochę głupi przykład, oczywiście w prawdziwym życiu miałbyś więcej testów niż te:

Załóżmy, że masz klasę, która zwraca posortowaną listę ciągów - powinieneś sprawdzić, czy wynik jest posortowany, a nie jak to faktycznie sortuje tę listę. Możesz rozpocząć wdrażanie za pomocą jednego algorytmu, który po prostu sortuje listę. Po zakończeniu test nie musi się zmieniać, jeśli zmienisz algorytm sortowania. W tym momencie masz jeden test (zakładając, że sortowanie jest osadzone w twojej klasie):

  1. Czy mój wynik jest posortowany?

Powiedzmy teraz, że chcesz dwóch algorytmów (być może jeden jest bardziej wydajny w niektórych okolicznościach, ale nie w innych), a następnie każdy algorytm może (i ogólnie powinien) być dostarczony przez inną klasę, a twoja klasa wybiera z nich - możesz sprawdzić, czy to się dzieje wybrane przez Ciebie scenariusze wykorzystujące symulacje, ale oryginalny test jest nadal ważny, a ponieważ weryfikujemy jedynie możliwe do zaobserwowania zachowanie / stan, nie trzeba go zmieniać. Kończysz 3 testy:

  1. Czy mój wynik jest posortowany?
  2. Biorąc pod uwagę scenariusz (powiedzmy, że początkowa lista jest prawie posortowana na początek), czy jest wywołanie do klasy, która sortuje łańcuchy przy użyciu algorytmu X?
  3. Biorąc pod uwagę scenariusz (początkowa lista jest w losowej kolejności), czy wywołano klasę, która sortuje łańcuchy przy użyciu algorytmu Y?

Alternatywą byłoby rozpoczęcie testowania prywatnego kodu w twojej klasie - nic z tego nie zyskujesz - powyższe testy mówią mi wszystko, co muszę wiedzieć, jeśli chodzi o testy jednostkowe. Dodając testy prywatne, budujesz sobie prostą kurtkę, o ile więcej pracy byś zrobił, gdybyś nie tylko sprawdził, że wynik został posortowany, ale także jak został posortowany?

Testy (tego typu) powinny się zmieniać tylko wtedy, gdy zmienia się zachowanie, rozpocząć pisanie testów na prywatnym kodzie i to wychodzi poza okno.

FinnNk
źródło
1
Być może istnieje nieporozumienie co do znaczenia słowa „prywatny”. W naszym systemie 99% kodu jest „prywatne”, wtedy mamy małe API do automatyzacji / zdalnego sterowania jednym z elementów systemu. Mam na myśli testowanie kodu wszystkich pozostałych modułów przez jednostkę.
Wizard79
4

jest jeszcze jeden powód: w hipotetycznym przypadku musiałbym wybrać między testowaniem jednostkowym zewnętrznego interfejsu API a częściami prywatnymi, wybrałbym części prywatne.

Jeśli każda część prywatna jest objęta testem, API składający się z tych części prywatnych również powinien być objęty prawie w 100%, z wyjątkiem samej górnej warstwy. Ale prawdopodobnie będzie to cienka warstwa.

Z drugiej strony, podczas testowania API, naprawdę ciężko jest w pełni pokryć wszystkie możliwe ścieżki kodu.

stijn
źródło
+1 „z drugiej strony ...” Ale jeśli nic innego, dodaj testy, w których porażka najbardziej zaszkodzi.
Tony Ennis,
2

Trudno jest skłonić ludzi do zaakceptowania testów jednostkowych, ponieważ wydaje się to stratą czasu („moglibyśmy kodować kolejny projekt zarabiania pieniędzy!”) Lub rekurencyjne („A potem musimy napisać przypadki testowe dla przypadków testowych!”) Jestem winny powiedzieć jedno i drugie.

Gdy pierwszy raz znajdziesz błąd, musisz zmierzyć się z prawdą, że nie jesteś idealny (jak szybko my programiści zapominamy!) I zaczynasz: „Hmmm”.


Innym aspektem testowania jednostkowego jest to, że kod musi być napisany, aby był testowalny. Uświadomienie sobie, że jakiś kod można łatwo przetestować, a jakiś kod nie czyni dobrego programisty „Hmmm”.


Czy zapytałeś współpracownika, dlaczego testowanie jednostkowe jest przydatne tylko w przypadku interfejsów API skierowanych do zewnątrz?


Jednym ze sposobów pokazania wartości testowania jednostkowego jest poczekanie na wystąpienie nieprzyjemnego błędu, a następnie pokazanie, w jaki sposób testowanie jednostkowe mogło temu zapobiec. To nie ma zamiaru pocierać ich w twarz, to znaczy, w ich umysłach, przenieść testowanie jednostek z Teorii Wieży z Kości Słoniowej do rzeczywistości w okopach.

Innym sposobem jest poczekanie, aż ten sam błąd wystąpi dwukrotnie . „Uhhh, cóż, szefie, dodaliśmy kod w celu przetestowania wartości zerowej po problemie z zeszłego tygodnia, ale tym razem użytkownik wprowadził pustą rzecz!”


Dawaj dobry przykład. Napisz testy jednostkowe dla swojego kodu, a następnie pokaż swojemu szefowi wartość. Sprawdź, czy pewnego dnia szef zadzwoni do pizzy na lunch i wygłosi prezentację.


Wreszcie, nie mogę ci powiedzieć, jaką ulgę odczuwam, kiedy mamy zamiar naciskać, aby szturchnąć i otrzymuję zielony pasek z testów jednostkowych.

Tony Ennis
źródło
2

Istnieją dwa rodzaje kodu prywatnego: kod prywatny, który jest wywoływany przez kod publiczny (lub kod prywatny, który jest wywoływany przez kod prywatny, który jest wywoływany przez kod publiczny (lub ...)) i kod prywatny, który ostatecznie nie jest wywoływany przez publiczny kod.

Ten pierwszy jest już testowany przez testy kodu publicznego. Tego ostatniego nie można w ogóle wywołać, dlatego należy go usunąć, a nie przetestować.

Zauważ, że kiedy robisz TDD, niemożliwe jest istnienie niepoddanego testom prywatnego kodu.

Jörg W Mittag
źródło
W naszym systemie 99% kodu jest trzeciego rodzaju : prywatny, nie wywoływany przez kod publiczny i niezbędny dla systemu (tylko minimalna część naszego systemu ma zewnętrzny, publiczny interfejs API).
Wizard79
1
„Zauważ, że kiedy robisz TDD, niemożliwe jest istnienie niepoddanego testom prywatnego kodu”. <- usuń przypadek testowy, nie wiedząc, że test jest jedynym testem obejmującym określoną gałąź. OK, to bardziej „obecnie nieprzetestowany” kod, ale łatwo jest zobaczyć późniejsze trywialne refaktoryzowanie zmieniające ten kod ... tylko zestaw testów go nie obejmuje.
Frank Shearar
2

Testowanie jednostkowe polega na testowaniu jednostek twojego kodu. To do Ciebie należy określenie, czym jest jednostka. Twoi współpracownicy definiują jednostki jako elementy API.

W każdym razie testowanie interfejsu API powinno również skutkować korzystaniem z kodu prywatnego. Jeśli zdefiniujesz pokrycie kodu jako wskaźnik postępu testów jednostkowych, zakończysz testowanie całego kodu. Jeśli część kodu nie została osiągnięta, daj swoim współpracownikom trzy opcje:

  • zdefiniuj kolejny przypadek testowy obejmujący tę część,
  • analizować kod w celu uzasadnienia, dlaczego nie może być objęty testem jednostkowym, ale powinien zostać objęty w innych sytuacjach,
  • usuń martwy kod, który nie został objęty, nieuzasadniony.
mouviciel
źródło
W naszym systemie interfejs API jest tylko minimalną częścią, która umożliwia automatyzację / zdalne sterowanie aplikacjami innych firm. Testowanie tylko interfejsu API obejmuje 1% pokrycia kodu ...
Wizard79