Testy jednostkowe - pierwsze kroki

14

Właśnie zaczynam od testów jednostkowych, ale nie jestem pewien, czy naprawdę rozumiem sens tego wszystkiego. Czytam tutoriale i książki na ten temat, ale mam tylko dwa krótkie pytania:

  1. Myślałem, że celem testów jednostkowych jest przetestowanie napisanego przez nas kodu. Wydaje mi się jednak, że aby móc uruchomić test, musimy zmienić oryginalny kod, w którym to momencie tak naprawdę nie testujemy napisanego przez nas kodu, ale kod, który napisaliśmy do testowania.

  2. Większość naszych kodów opiera się na źródłach zewnętrznych. Jednak po przefakturowaniu naszego kodu, nawet złamałby oryginalny kod, nasze testy nadal działałyby dobrze, ponieważ zewnętrzne źródła to tylko pomyłki wewnątrz naszych przypadków testowych. Czy to nie przeczy celowi testów jednostkowych?

Przepraszam, jeśli tutaj brzmię głupio, ale myślałem, że ktoś może mnie trochę oświecić.

Z góry dziękuję.

treecoder
źródło

Odpowiedzi:

7

Moje 0,02 $ ... to jest trochę subiektywne, więc weź trochę ziarenka soli, ale mam nadzieję, że sprawi, że zaczniesz myśleć i / lub zainicjować dialog:

  1. Głównym celem testów jednostkowych jest dla mnie upewnienie się, że kod, który napisałeś, spełnia umowy i zawiera przypadki, w których kod ma zostać wypełniony. Dzięki przeprowadzonym testom jednostkowym możesz lepiej upewnić się, że kiedy ty (lub ktoś w przyszłości) zmienisz kod swojego kodu, wszyscy zewnętrzni odbiorcy twojego kodu pozostaną nienaruszeni, jeśli masz odpowiedni zasięg. (przynajmniej w takim stopniu, w jakim zamierzasz pozostać nienaruszonym).

    W większości przypadków powinieneś być w stanie napisać kod, który można zarówno wysłać do produkcji, jak i łatwo przetestować w jednostce. Dobrym początkiem może być przyjrzenie się wzorcom i strukturom wstrzykiwania zależności. (Lub inne filozofie dla twojego języka / wybranej platformy).

  2. Prawdą jest, że zewnętrzne implementacje mogą wpływać na Twój kod. Jednak sprawdzenie , czy kod działa poprawnie jako część większego systemu, jest funkcją testowania integracji . (Który można również zautomatyzować przy różnym stopniu wysiłku).

    Idealnie twój kod powinien opierać się tylko na umowach API dowolnych komponentów stron trzecich, co oznaczałoby, że dopóki twoje symulacje spełniają poprawne API, twoje testy jednostkowe nadal zapewniają wartość.

    To powiedziawszy, z łatwością przyznam, że zdarzały się chwile, kiedy zrezygnowałem z testów jednostkowych na rzecz samych testów integracyjnych, ale były to tylko przypadki, w których mój kod musiał tak obficie współdziałać z komponentami innych firm ze słabo udokumentowanymi interfejsami API. (tj. raczej wyjątek niż reguła).

Charlie
źródło
5
  1. Spróbuj najpierw napisać testy. W ten sposób będziesz mieć solidną podstawę do zachowania kodu, a test stanie się umową o wymagane zachowanie kodu. Zatem zmiana kodu w celu zaliczenia testu staje się „zmianą kodu w celu wypełnienia umowy zaproponowanej przez test” zamiast „zmianą kodu w celu zaliczenia testu”.
  2. Uważaj na różnicę między skrótami a próbami. Brak wpływu na zmiany w kodzie jest charakterystycznym zachowaniem kodów pośredniczących, ale nie próbnych. Zacznijmy od definicji makiety:

    Próbny obiekt zastępuje prawdziwy obiekt w warunkach testowych i umożliwia weryfikację wywołań (interakcji) względem siebie jako część testu systemowego lub jednostkowego.

    - Sztuka testowania jednostkowego

Zasadniczo twoje kpiny powinny sprawdzać wymagane zachowanie twoich interakcji. Tak więc, jeśli twoja interakcja z bazą danych zakończy się niepowodzeniem po refaktoryzacji, twój test z próbą również powinien zakończyć się niepowodzeniem. Ma to oczywiście ograniczenia, ale przy starannym planowaniu twoje kpiny zrobią znacznie więcej niż tylko „siedzenie tam” i nie „pokonają celu testów jednostkowych”.

ruhsuzbaykus
źródło
1

Zadanie dobrego pytania nie jest w żaden sposób głupie.

Odpowiem na twoje pytania.

  1. Celem testów jednostkowych nie jest testowanie kodu, który już napisałeś . Nie ma pojęcia o czasie. Tylko w TDD powinieneś najpierw przetestować, ale to nie dotyczy ściśle żadnego rodzaju testów jednostkowych. Chodzi o to, aby móc automatycznie i skutecznie przetestować swój program na poziomie klasy. Robisz to, co musisz, aby się tam dostać, nawet jeśli oznacza to zmianę kodu. Pozwól, że powiem ci sekret - często to oznacza.
  2. Kiedy piszesz test, masz 2 podstawowe opcje, aby upewnić się, że test jest poprawny:

    • Zmieniaj dane wejściowe dla każdego testu
    • Napisz przypadek testowy, który zapewni, że Twój program działa poprawnie, a następnie napisz odpowiedni przypadek testowy, który zapewni, że Twój program nie działa tak, jak powinien

    Oto przykład:

    TEST(MyTest, TwoPlusTwoIsFour) {
        ASSERT_EQ(4, 2+2);
    }
    
    TEST(MyTest, TwoPlusThreeIsntFour) {
        ASSERT_NE(4, 2+3);
    }
    

    Ponadto, jeśli testujesz logikę jednostkową w swoim kodzie (a nie bibliotekach stron trzecich), to w porządku, że nie martwisz się o złamanie innego kodu w tym kontekście. Zasadniczo testujesz sposób, w jaki twoja logika się zawija i używa narzędzi zewnętrznych, co jest klasycznym scenariuszem testowania.

Po zakończeniu testowania na poziomie klasy możesz przejść do testowania integracji między swoimi klasami (z tego, co rozumiem, mediatorami) a bibliotekami stron trzecich. Testy te nazywane są testami integracyjnymi i nie używają próbnych, lecz konkretnych implementacji wszystkich klas. Są nieco wolniejsze, ale wciąż bardzo ważne!

Yam Marcovic
źródło
1

Wygląda na to, że masz monolityczną aplikację, która robi wszystko void main(), od dostępu do bazy danych po generowanie danych wyjściowych. Aby rozpocząć właściwe testowanie jednostek, należy wykonać kilka kroków.

1) Znajdź fragment kodu, który został napisany / wklejony więcej niż jeden raz. Nawet jeśli to jest po prostu string fullName = firstName + " " + lastName. Podziel to na metodę, taką jak:

private static string GetFullName (firstName, lastName)
{
    return firstName + " " + lastName;
}

Teraz masz fragment kodu, który można przetestować w jednostce, jakkolwiek może być to trywialne. Napisz do tego test jednostkowy. Opłucz i powtórz ten proces. W końcu skończysz z mnóstwem logicznie pogrupowanych metod i będziesz w stanie wyodrębnić z niego wiele klas. Większość tych klas będzie można przetestować.

Jako bonus, po wyodrębnieniu wielu klas, możesz wyodrębnić z nich interfejsy i zaktualizować swój program, aby komunikował się z interfejsami zamiast z samymi obiektami. W tym momencie możesz korzystać z frameworku / korkowania (lub nawet własnych fałszywych ręcznie podróbek) bez zmiany programu. Jest to bardzo przydatne po wyodrębnieniu zapytań bazy danych do klasy (lub wielu), ponieważ teraz można sfałszować dane, które zapytanie powinno zwrócić.

Bryan Boettcher
źródło
0

Jakiś sprytny facet powiedział: „Jeśli trudno jest przetestować, to prawdopodobnie jest to zły kod”. Właśnie dlatego przepisywanie kodu nie jest niczym złym, aby móc go wypisać. Kod z zewnętrznymi zależnościami jest BARDZO TWARDY do przetestowania, reprezentuje ryzyko w kodzie i należy go unikać, o ile to możliwe, i koncentrować się na obszarach integracji specyficznych dla twojego kodu, fx. klasy typu elewacji / bramy. Dzięki temu zmiana komponentu zewnętrznego będzie o wiele łatwiejsza.

Morten
źródło