Nowy w testowaniu jednostkowym, jak pisać świetne testy? [Zamknięte]

267

Jestem całkiem nowy w świecie testów jednostkowych i właśnie postanowiłem w tym tygodniu dodać zasięg testowy do mojej istniejącej aplikacji.

To ogromne zadanie, głównie ze względu na liczbę klas do przetestowania, ale także dlatego, że pisanie testów jest dla mnie zupełnie nowe.

Pisałem już testy dla wielu klas, ale teraz zastanawiam się, czy robię to dobrze.

Kiedy piszę testy dla metody, mam wrażenie, że przepisuję po raz drugi to, co już napisałem w samej metodzie.
Moje testy wydają się tak ściśle związane z metodą (testowanie całej ścieżki kodowej, spodziewając się, że niektóre wewnętrzne metody będą wywoływane kilka razy, z pewnymi argumentami), że wydaje się, że jeśli kiedykolwiek zmienię metodę, testy nie powiodą się, nawet jeśli ostateczne zachowanie metody nie uległo zmianie.

To tylko uczucie i, jak powiedziano wcześniej, nie mam doświadczenia w testowaniu. Gdyby niektórzy bardziej doświadczeni testerzy mogli udzielić mi porady, jak napisać świetne testy dla istniejącej aplikacji, byłoby to bardzo mile widziane.

Edycja: Chciałbym podziękować przepełnieniu stosu, miałem świetne dane wejściowe w mniej niż 15 minut, które odpowiadały więcej godzinom czytania online, które właśnie zrobiłem.

pixelastic
źródło
1
To najlepsza książka do testowania jednostkowego: manning.com/osherove Wyjaśnia wszystkie najlepsze praktyki, czynności do wykonania i nie dotyczące testowania jednostkowego.
Ervi B,
Jedną z tych wszystkich odpowiedzi, które pomija się, jest to, że testowanie jednostkowe jest jak dokumentacja. Ergo, jeśli napiszesz funkcję, udokumentujesz jej intencję, opisując jej dane wejściowe i wyjściowe (i ewentualnie skutki uboczne). Test jednostkowy ma zatem na celu sprawdzenie tego. A jeśli później (lub ktoś inny) wprowadzisz zmiany w kodzie, dokumenty powinny wyjaśniać granice możliwych zmian, a testy jednostkowe upewniają się, że granice są zachowane.
Thomas Tempelmann,

Odpowiedzi:

187

Moje testy wydają się tak ściśle związane z metodą (testowanie całej ścieżki kodowej, spodziewając się, że niektóre wewnętrzne metody będą wywoływane kilka razy, z pewnymi argumentami), że wydaje się, że jeśli kiedykolwiek zmienię metodę, testy nie powiodą się, nawet jeśli ostateczne zachowanie metody nie uległo zmianie.

Myślę, że robisz to źle.

Test jednostkowy powinien:

  • przetestuj jedną metodę
  • podać konkretne argumenty dla tej metody
  • sprawdź, czy wynik jest zgodny z oczekiwaniami

Nie powinien zaglądać do metody, aby zobaczyć, co robi, więc zmiana elementów wewnętrznych nie powinna spowodować niepowodzenia testu. Nie powinieneś bezpośrednio sprawdzać, czy wywoływane są metody prywatne. Jeśli chcesz dowiedzieć się, czy Twój prywatny kod jest testowany, skorzystaj z narzędzia do pokrycia kodu. Ale nie przejmuj się tym: 100% ubezpieczenia nie jest wymagane.

Jeśli twoja metoda wywołuje metody publiczne w innych klasach, a wywołania te są gwarantowane przez interfejs, możesz przetestować, czy te wywołania są wykonywane przy użyciu frameworku.

Nie należy używać samej metody (ani żadnego używanego przez nią kodu wewnętrznego) do dynamicznego generowania oczekiwanego wyniku. Oczekiwany wynik powinien być zakodowany na stałe w twoim przypadku testowym, aby nie zmieniał się, gdy zmienia się implementacja. Oto uproszczony przykład tego, co powinien zrobić test jednostkowy:

testAdd()
{
    int x = 5;
    int y = -2;
    int expectedResult = 3;
    Calculator calculator = new Calculator();
    int actualResult = calculator.Add(x, y);
    Assert.AreEqual(expectedResult, actualResult);
}

Zauważ, że sposób obliczania wyniku nie jest sprawdzany - tylko że wynik jest poprawny. Dodawaj coraz więcej prostych przypadków testowych, takich jak powyższe, aż obejmiesz jak najwięcej scenariuszy. Skorzystaj z narzędzia do pokrywania kodu, aby sprawdzić, czy przegapiłeś jakieś interesujące ścieżki.

Mark Byers
źródło
13
Wielkie dzięki, twoja odpowiedź była bardziej kompletna. Teraz lepiej rozumiem, do czego tak naprawdę służą fałszywe obiekty: nie muszę potwierdzać każdego wywołania innych metod, tylko tych odpowiednich. Nie muszę też wiedzieć, JAK się to robi, ale że tak.
pixelastic
2
I z szacunkiem myśleć ty robisz to źle. Testy jednostkowe dotyczą przepływu wykonania kodu (testy białej skrzynki). Testowanie czarnej skrzynki (co sugerujesz) jest zwykle techniką stosowaną w testach funkcjonalnych (testy systemowe i testowe).
Wes
1
„Test jednostkowy powinien przetestować jedną metodę”. Właściwie to się nie zgadzam. Test jednostkowy powinien przetestować jedną logiczną koncepcję. Chociaż jest to często przedstawiane jako jedna metoda, nie zawsze tak jest
robertmain
35

Do testów jednostkowych uznałem, że zarówno Test Driven (najpierw testy, drugi kod), jak i pierwszy kod, drugi test, są niezwykle przydatne.

Zamiast pisać kod, a następnie pisać test. Napisz kod, a następnie spójrz na to, co MYŚLISZ, że kod powinien robić. Pomyśl o wszystkich zamierzonych zastosowaniach, a następnie napisz test dla każdego. Testy pisania są szybsze, ale bardziej zaangażowane niż samo kodowanie. Testy powinny przetestować intencję. Pomyśl także o zamiarach znalezienia narożnych skrzynek w fazie pisania testu. I oczywiście podczas pisania testów może się zdarzyć, że jedno z niewielu zastosowań powoduje błąd (coś, co często znajduję i jestem bardzo zadowolony, że ten błąd nie uszkodził danych i nie został sprawdzony).

Jednak testowanie przypomina prawie dwukrotne kodowanie. W rzeczywistości miałem aplikacje, w których było więcej kodu testowego (ilości) niż kod aplikacji. Jednym z przykładów była bardzo złożona maszyna stanu. Musiałem się upewnić, że po dodaniu do niego większej logiki, wszystko zawsze działało na wszystkich poprzednich przypadkach użycia. A ponieważ te przypadki były dość trudne do prześledzenia, patrząc na kod, skończyło się na tak dobrym pakiecie testowym dla tej maszyny, że byłem pewien, że nawet się nie złamie po wprowadzeniu zmian, a testy uratowały mi tyłek kilka razy . A ponieważ użytkownicy lub testerzy znaleźli błędy w przepływach lub przypadkach narożnych, które nie zostały uwzględnione, zgadnij, co dodało do testów i nigdy więcej się nie powtórzyło. To naprawdę dało użytkownikom pewność co do mojej pracy i uczyniło całość super stabilną. A kiedy trzeba go było przepisać ze względu na wydajność, zgadnij co,

Wszystkie proste przykłady, takie jak, function square(number)są świetne i są prawdopodobnie złymi kandydatami do spędzania dużo czasu na testowaniu. Te, które wykonują ważną logikę biznesową, czyli tam, gdzie ważne jest testowanie. Przetestuj wymagania. Nie tylko testuj hydraulikę. Jeśli wymagania się zmienią, zgadnij co, testy też muszą.

Testowanie nie powinno dosłownie testować tej funkcji trzy razy przywołany pasek funkcji foo. To jest złe. Sprawdź, czy wynik i skutki uboczne są prawidłowe, a nie mechanika wewnętrzna.

Dmitriy Likhten
źródło
2
Ładna odpowiedź, dała mi pewność, że pisanie testów po kodzie może być nadal przydatne i możliwe.
pixelastic
2
Doskonały niedawny przykład. Miałem bardzo prostą funkcję. Przekaż to prawda, robi jedną rzecz, fałsz robi inną. BARDZO PROSTY. Miałem 4 testy sprawdzające, aby upewnić się, że funkcja robi to, co zamierza. Trochę zmieniam zachowanie. Przeprowadź testy, problem POW. Zabawne jest to, że podczas korzystania z aplikacji problem nie pojawia się, tylko w złożonym przypadku. Przypadek testowy go znalazł i zaoszczędziłem sobie godzin pracy.
Dmitriy Likhten,
„Testy powinny przetestować intencję”. Myślę, że to podsumowuje, że powinieneś przejrzeć zamierzone zastosowania kodu i upewnić się, że kod może je uwzględnić. Wskazuje również na zakres tego, co test powinien faktycznie przetestować, oraz na pomysł, że po dokonaniu zmiany kodu w tej chwili możesz nie zastanowić się, w jaki sposób ta zmiana wpływa na wszystkie zalecane zastosowania kodu - test broni się przed zmianą, która nie spełnia wszystkich zamierzonych przypadków użycia.
Greenstick,
18

Warto zauważyć, że modyfikowanie testów jednostkowych w istniejącym kodzie jest znacznie trudniejsze niż kierowanie tworzeniem tego kodu za pomocą testów. To jedno z głównych pytań dotyczących starszych aplikacji ... jak przeprowadzić test jednostkowy? Ten został poproszony wiele razy wcześniej (więc może być zamknięty jako pytanie dupe), a ludzie zazwyczaj kończy się tutaj:

Przeniesienie istniejącego kodu do Test Driven Development

Popieram zalecenie książki przyjętej odpowiedzi, ale poza tym w odpowiedziach znajduje się więcej informacji.

David
źródło
3
Jeśli piszesz testy pierwszy lub drugi, wszystko jest w porządku, ale pisząc testy, upewniasz się, że kod jest testowalny, dzięki czemu MOŻESZ pisać testy. Skończyło się na tym, że „jak mogę to przetestować” często, co samo w sobie powoduje, że lepiej napisać kod. Modernizacja przypadków testowych jest zawsze dużym nie-nie. Bardzo trudny. To nie jest problem czasowy, to kwestia ilości i testowalności. Nie mogę teraz podejść do mojego szefa i powiedzieć, że chcę napisać przypadki testowe dla naszych ponad tysiąca tabel i zastosowań, to już za dużo, zajęłoby mi to rok, a niektóre logiki / decyzje zostały zapomniane. Więc nie
odkładaj
2
Przypuszczalnie przyjęta odpowiedź uległa zmianie. Odpowiedź Linx zaleca sztukę testowania jednostek przez Roy Osherove, manning.com/osherove
thelem
15

Nie pisz testów, aby uzyskać pełne pokrycie kodu. Napisz testy, które gwarantują twoje wymagania. Możesz odkryć niepotrzebne ścieżki kodowe. I odwrotnie, jeśli są konieczne, muszą spełnić pewien wymóg; znajdź to, co to jest i przetestuj wymaganie (nie ścieżkę).

Utrzymuj małe testy: jeden test na każde wymaganie.

Później, gdy musisz wprowadzić zmiany (lub napisać nowy kod), spróbuj najpierw napisać jeden test. Tylko jeden. Następnie zrobisz pierwszy krok w rozwoju opartym na testach.

Jon Reid
źródło
Dzięki temu sensowne jest przeprowadzanie tylko małych testów dla małych wymagań, pojedynczo. Wyciągnięta lekcja.
pixelastic
13

Testy jednostkowe dotyczą wyników uzyskanych z funkcji / metody / aplikacji. Nie ma znaczenia, w jaki sposób powstaje wynik, liczy się tylko to, że jest poprawny. Dlatego twoje podejście do liczenia wywołań metod wewnętrznych i takie jest błędne. To, co zwykle robię, to usiąść i napisać, co metoda powinna zwrócić, biorąc pod uwagę określone wartości wejściowe lub określone środowisko, a następnie napisać test, który porównuje rzeczywistą wartość zwróconą z tym, co wymyśliłem.

fresskoma
źródło
Dzięki ! Miałem wrażenie, że robię to źle, ale lepiej, żeby ktoś mi powiedział.
pixelastic
8

Spróbuj napisać test jednostkowy przed napisaniem metody, którą zamierza przetestować.

To z pewnością zmusi cię do nieco innego myślenia o tym, jak się to robi. Nie będziesz miał pojęcia, jak ta metoda zadziała, tylko co ma zrobić.

Zawsze powinieneś testować wyniki metody, a nie to, w jaki sposób metoda uzyskuje te wyniki.

Justin Niessner
źródło
Tak, chciałbym móc to zrobić, tyle że metody są już napisane. Chcę je tylko przetestować. W przyszłości napiszę testy przed metodami, tho.
pixelastic
2
@pixelastic udawać, że metody nie zostały napisane?
zaangażowanieandroider
4

testy mają poprawić łatwość konserwacji. Jeśli zmienisz metodę, a test się zepsuje, może to być dobra rzecz. Z drugiej strony, jeśli spojrzysz na swoją metodę jako czarną skrzynkę, to nie powinno mieć znaczenia, co jest w środku metody. Faktem jest, że musisz kpić z niektórych testów, aw takich przypadkach naprawdę nie możesz traktować tej metody jako czarnej skrzynki. Jedyne, co możesz zrobić, to napisać test integracyjny - ładujesz w pełni utworzoną instancję testowanej usługi i każesz jej działać tak, jakby działała w Twojej aplikacji. Następnie możesz potraktować to jak czarne pudełko.

When I'm writing tests for a method, I have the feeling of rewriting a second time what I          
already wrote in the method itself.
My tests just seems so tightly bound to the method (testing all codepath, expecting some    
inner methods to be called a number of times, with certain arguments), that it seems that
if I ever refactor the method, the tests will fail even if the final behavior of the   
method did not change.

Wynika to z faktu, że piszesz testy po napisaniu kodu. Jeśli zrobiłeś to na odwrót (najpierw napisałeś testy), nie czułbyś się w ten sposób.

hvgotcodes
źródło
Dzięki za przykład czarnej skrzynki, nie myślałem w ten sposób. Chciałbym wcześniej odkryć testy jednostkowe, ale niestety tak nie jest i utknąłem ze starszą aplikacją do dodawania testów. Czy nie ma sposobu na dodanie testów do istniejącego projektu bez poczucia, że ​​są zepsute?
pixelastic
1
Pisanie testów po różni się od pisania testów wcześniej, więc utkniesz z tym. możesz jednak skonfigurować testy tak, aby najpierw zawiodły, a następnie sprawdziły, wystawiając klasę na działanie ... zrób coś takiego, poddając test instancji po niepowodzeniu testu. To samo z próbkami - początkowo próbka nie ma oczekiwań i zawiedzie, ponieważ testowana metoda zrobi coś z próbką, a następnie przejdzie test. Nie zdziwiłbym się, gdyby w ten sposób znaleźć wiele błędów.
hvgotcodes
bądź też bardzo konkretny ze swoimi oczekiwaniami. Nie twierdz, że test zwraca obiekt, sprawdź, czy obiekt ma różne wartości. Sprawdź, czy kiedy wartość ma być zerowa, tak właśnie jest. Możesz też trochę to rozerwać, robiąc pewne refaktoryzacje, które chciałeś zrobić, po dodaniu kilku testów.
hvgotcodes