unit-testing
Ogólne zasady testowania jednostkowego we wszystkich językach
Szukaj…
Wprowadzenie
Rozpoczynając od testowania jednostkowego, pojawiają się różnego rodzaju pytania:
Co to są testy jednostkowe? Co to jest SetUp and TearDown? Jak radzić sobie z zależnościami? Dlaczego w ogóle przeprowadzane są testy jednostkowe? Jak wykonać dobre testy jednostkowe?
W tym artykule znajdziesz odpowiedzi na wszystkie te pytania, dzięki czemu możesz rozpocząć testy jednostkowe w dowolnym języku.
Uwagi
Co to są testy jednostkowe?
Testowanie jednostkowe to testowanie kodu w celu upewnienia się, że wykonuje ono zadanie, które ma wykonać. Testuje kod na najniższym możliwym poziomie - poszczególnych metodach twoich klas.
Co to jest jednostka?
Dowolny dyskretny moduł kodu, który można przetestować osobno. Większość klas czasowych i ich metody. Ta klasa jest ogólnie określana jako „Class Under Test” (CUT) lub „System Under Test” (SUT)
Różnica między testowaniem jednostkowym a testowaniem integracyjnym
Testowanie jednostkowe polega na testowaniu pojedynczej klasy w izolacji, całkowicie niezależnie od jej rzeczywistych zależności. Testy integracyjne to testowanie pojedynczej klasy wraz z jedną lub kilkoma rzeczywistymi zależnościami.
SetUp and TearDown
Po wykonaniu metoda SetUp jest uruchamiana przed każdym testem jednostkowym, a TearDown po każdym teście.
Zasadniczo dodajesz wszystkie wymagane kroki w SetUp i wszystkie kroki czyszczenia w TearDown. Ale wykonujesz tę metodę tylko wtedy, gdy te kroki są potrzebne dla każdego testu. Jeśli nie, to kroki te są podejmowane w ramach konkretnych testów w sekcji „aranżuj”.
Jak radzić sobie z zależnościami
Wiele razy klasa jest zależna od innych klas do wykonywania swoich metod. Aby móc polegać na innych klasach, musisz je sfałszować. Możesz samodzielnie stworzyć te klasy lub użyć frameworka izolacyjnego lub makiety. Framework izolacji to zbiór kodu, który umożliwia łatwe tworzenie fałszywych klas.
Fałszywe zajęcia
Każda klasa zapewniająca funkcjonalność wystarczającą do udawania, że jest to zależność potrzebna przez CUT. Istnieją dwa rodzaje podróbek: Stuby i Mocks.
- Kod pośredniczący: Fałszywy, który nie ma wpływu na pozytywny lub negatywny wynik testu i który istnieje wyłącznie w celu umożliwienia uruchomienia testu.
- Kpina: Fałszywy, który śledzi zachowanie CUT i pomyślnie przechodzi test na podstawie tego zachowania.
Dlaczego przeprowadzane są testy jednostkowe?
1. Testy jednostkowe wykryją błędy
Kiedy piszesz pełny zestaw testów, które określają oczekiwane zachowanie dla danej klasy, ujawnia się wszystko, co nie zachowuje się zgodnie z oczekiwaniami.
2. Testowanie jednostkowe pozwoli uniknąć błędów
Wprowadź zmianę, która wprowadza błąd, a Twoje testy mogą go wykryć przy następnym uruchomieniu testów.
3. Testy jednostkowe oszczędzają czas
Pisanie testów jednostkowych pomaga upewnić się, że kod działa od początku zgodnie z przeznaczeniem. Testy jednostkowe określają, co powinien zrobić Twój kod, dlatego nie będziesz tracić czasu na pisanie kodu, który robi rzeczy, których nie powinien robić. Nikt nie sprawdza w kodzie, czy nie wierzy, że działa, a ty musisz coś zrobić, aby myśleć, że to działa. Poświęć ten czas na napisanie testów jednostkowych.
4. Testy jednostkowe zapewniają spokój ducha
Możesz uruchomić wszystkie te testy i wiedzieć, że Twój kod działa tak, jak powinien. Znajomość stanu kodu, jego działanie oraz możliwość jego aktualizacji i ulepszania bez obaw to bardzo dobra rzecz.
5. Testy jednostkowe dokumentują prawidłowe użycie klasy
Testy jednostkowe stają się prostymi przykładami tego, jak działa Twój kod, czego oczekuje się od niego i właściwego sposobu używania testowanego kodu.
Ogólne zasady testowania jednostkowego
1. Aby zbudować test jednostkowy, należy przestrzegać zasady AAA
Zorganizować:
Skonfiguruj element do przetestowania. Podobnie jak zmienne, pola i właściwości, aby umożliwić uruchomienie testu, a także oczekiwany wynik.
Akt: Właściwie wywołaj metodę, którą testujesz
Zapewniać:
Wywołaj środowisko testowe, aby sprawdzić, czy wynik „działania” jest zgodny z oczekiwaniami.
2. Przetestuj jedną rzecz naraz
Wszystkie klasy powinny być testowane w izolacji. Nie powinny polegać na niczym innym niż na próbach i skrótach. Nie powinny zależeć od wyników innych testów.
3. Najpierw napisz proste testy „od środka”
Pierwsze testy, które napiszesz, powinny być najprostszymi testami. Powinny to być te, które w zasadzie i łatwo ilustrują funkcjonalność, którą próbujesz napisać. Następnie, po przejściu tych testów, powinieneś zacząć pisać bardziej skomplikowane testy, które testują krawędzie i granice twojego kodu.
4. Napisz testy sprawdzające krawędzie
Po przetestowaniu podstaw i upewnieniu się, że działa podstawowa funkcjonalność, należy przetestować krawędzie. Dobry zestaw testów bada zewnętrzne krawędzie tego, co może się stać z daną metodą.
Na przykład:
- Co się stanie, jeśli nastąpi przepełnienie?
- Co jeśli wartości spadną do zera lub poniżej?
- Co jeśli przejdą do MaxInt lub MinInt?
- Co zrobić, jeśli utworzysz łuk 361 stopni?
- Co się stanie, jeśli przekażesz pusty ciąg?
- Co się stanie, jeśli ciąg ma rozmiar 2 GB?
5. Testuj ponad granicami
Testy jednostkowe powinny przetestować obie strony danej granicy. Przekraczanie granic to miejsca, w których kod może zawieść lub działać w nieprzewidywalny sposób.
6. Jeśli możesz, przetestuj całe spektrum
Jeśli jest to praktyczne, przetestuj cały zestaw możliwości swojej funkcjonalności. Jeśli dotyczy typu wyliczeniowego, przetestuj funkcjonalność z każdym z elementów wyliczenia. Testowanie każdej możliwości może być niepraktyczne, ale jeśli możesz przetestować każdą możliwość, zrób to.
7. Jeśli to możliwe, uwzględnij każdą ścieżkę kodu
Ten jest również trudny, ale jeśli twój kod jest przeznaczony do testowania i korzystasz z narzędzia do pokrycia kodu, możesz upewnić się, że każda linia kodu jest objęta testami jednostkowymi przynajmniej raz. Pokrycie każdej ścieżki kodu nie gwarantuje, że nie ma żadnych błędów, ale z pewnością daje cenne informacje na temat stanu każdej linii kodu.
8. Napisz testy, które ujawniają błąd, a następnie napraw go
Jeśli znajdziesz błąd, napisz test, który go ujawnia. Następnie możesz łatwo naprawić błąd, debugując test. Następnie masz fajny test regresji, aby upewnić się, że jeśli błąd wróci z jakiegokolwiek powodu, od razu się dowiesz. Naprawdę łatwo jest naprawić błąd, gdy masz prosty, prosty test do uruchomienia w debuggerze.
Dodatkową korzyścią jest to, że przetestowałeś swój test. Ponieważ widziałeś, że test się nie powiódł, a potem, gdy go zdałeś, wiesz, że test jest ważny, ponieważ udowodniono, że działa poprawnie. To sprawia, że jest to jeszcze lepszy test regresji.
9. Uczyń każdy test niezależnym od siebie
Testy nigdy nie powinny zależeć od siebie. Jeśli testy muszą przebiegać w określonej kolejności, musisz je zmienić.
10. Napisz jeden test na test
Powinieneś napisać jeden aser na test. Jeśli nie możesz tego zrobić, zmodyfikuj kod, aby zdarzenia SetUp i TearDown zostały użyte do poprawnego utworzenia środowiska, tak aby każdy test mógł być uruchamiany osobno.
11. Jasno nazwij swoje testy. Nie bój się długich nazwisk
Ponieważ wykonujesz jeden test na test, każdy test może być bardzo specyficzny. Dlatego nie bój się używać długich, pełnych nazw testowych.
Długa pełna nazwa pozwala natychmiast dowiedzieć się, który test się nie powiódł i dokładnie, co test chciał zrobić.
Długie, wyraźnie nazwane testy mogą również dokumentować testy. Test o nazwie „DividedByZeroShouldThrowException” dokumentuje dokładnie, co robi kod, gdy próbujesz podzielić przez zero.
12. Sprawdź, czy każdy zgłoszony wyjątek jest rzeczywiście zgłaszany
Jeśli Twój kod zgłasza wyjątek, napisz test, aby upewnić się, że każdy zgłoszony wyjątek zostanie zgłoszony w odpowiednim momencie.
13. Unikaj używania CheckTrue lub Assert.IsTrue
Unikaj sprawdzania warunku boolowskiego. Na przykład zamiast tego, jeśli sprawdzając, czy dwie rzeczy są równe z CheckTrue lub Assert.IsTrue, zamiast tego użyj CheckEquals lub Assert.IsEqual. Dlaczego? Z tego powodu:
CheckTrue (oczekiwany, aktualny) Zgłasza to coś w rodzaju: „Niektóre testy nie powiodły się: oczekiwano, że to prawda, ale rzeczywisty wynik był fałszywy”
To nic ci nie mówi.
CheckEquals (oczekiwany, aktualny)
To powie ci coś takiego: „Niektóre testy nie powiodły się: oczekiwano 7, ale rzeczywisty wynik to 3”.
Używaj CheckTrue lub Assert.IsTrue tylko wtedy, gdy oczekiwana wartość jest w rzeczywistości wartością logiczną.
14. Ciągle przeprowadzaj testy
Przeprowadź testy podczas pisania kodu. Twoje testy powinny działać szybko, umożliwiając ich uruchomienie nawet po niewielkich zmianach. Jeśli nie możesz uruchomić testów w ramach normalnego procesu programowania, oznacza to, że coś jest nie tak. Testy jednostkowe powinny przebiegać niemal natychmiast. Jeśli nie, to prawdopodobnie dlatego, że nie uruchamiasz ich w izolacji.
15. Uruchom testy w ramach każdej automatycznej kompilacji
Tak jak powinieneś przeprowadzać testy podczas rozwoju, powinny one również stanowić integralną część procesu ciągłej integracji. Nieudany test powinien oznaczać, że twoja kompilacja jest zepsuta. Nie pozwól, by nieudane testy pozostały. Uznaj to za błąd kompilacji i napraw go natychmiast.
Przykład prostego testu jednostkowego w C #
W tym przykładzie przetestujemy metodę sumy prostego kalkulatora.
W tym przykładzie przetestujemy aplikację: ApplicationToTest. Ten ma klasę o nazwie Calc. Ta klasa ma metodę Sum ().
Metoda Sum () wygląda następująco:
public void Sum(int a, int b)
{
return a + b;
}
Test jednostkowy w celu przetestowania tej metody wygląda następująco:
[Testclass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
//Arrange
ApplicationToTest.Calc ClassCalc = new ApplicationToTest.Calc();
int expectedResult = 5;
//Act
int result = ClassCalc.Sum(2,3);
//Assert
Assert.AreEqual(expectedResult, result);
}
}