Jak dokładnie należy pisać testy jednostkowe bez obszernego kpienia?

80

Jak rozumiem, celem testów jednostkowych jest testowanie jednostek kodu osobno . To znaczy że:

  1. Nie powinny łamać się przez jakąkolwiek niepowiązaną zmianę kodu w innym miejscu w bazie kodu.
  2. Tylko jeden test jednostkowy powinien przełamać błąd w testowanej jednostce, w przeciwieństwie do testów integracyjnych (które mogą pęknąć w stosach).

Wszystko to implikuje, że każda zewnętrzna zależność testowanej jednostki powinna być wyśmiewana. Mam na myśli wszystkie zewnętrzne zależności , nie tylko „zewnętrzne warstwy”, takie jak sieć, system plików, baza danych itp.

Prowadzi to do logicznego wniosku, że praktycznie każdy test jednostkowy musi kpić . Z drugiej strony, szybkie wyszukiwanie w Google na temat kpienia ujawnia mnóstwo artykułów, które twierdzą, że „kpina jest zapachem kodu” i powinno się go w większości (choć nie całkowicie) unikać.

Teraz do pytania (pytań).

  1. Jak należy poprawnie pisać testy jednostkowe?
  2. Gdzie dokładnie leży granica między nimi a testami integracji?

Aktualizacja 1

Proszę wziąć pod uwagę następujący pseudo kod:

class Person {
    constructor(calculator) {}

    calculate(a, b) {
        const sum = this.calculator.add(a, b);

        // do some other stuff with the `sum`
    }
}

Czy test, który testuje Person.calculatemetodę bez kpiny z Calculatorzależności (biorąc pod uwagę, że Calculatorjest to lekka klasa, która nie ma dostępu do „świata zewnętrznego”) można uznać za test jednostkowy?

Alexander Lomia
źródło
10
Częścią tego jest po prostu doświadczenie projektowe, które przyjdzie z czasem. Dowiesz się, jak konstruować komponenty, aby nie miały wielu trudnych do wyszydzenia zależności. Oznacza to, że testowalność musi być drugorzędnym celem projektowym każdego oprogramowania. Cel ten jest znacznie łatwiejszy do spełnienia, jeśli test jest napisany przed kodem lub razem z nim, np. Przy użyciu TDD i / lub BDD.
amon
33
Umieść testy, które działają szybko i niezawodnie w jednym folderze. Umieść testy, które są powolne i potencjalnie kruche, w inne. Testy należy uruchamiać w pierwszym folderze tak często, jak to możliwe (dosłownie za każdym razem, gdy przerywasz pisanie, a kompilacja kodu jest idealna, ale nie wszystkie środowiska programistyczne obsługują to). Rzadziej przeprowadzaj wolniejsze testy (na przykład podczas przerwy na kawę). Nie martw się o nazwy jednostek i integracji. Zadzwoń do nich szybko i wolno, jeśli chcesz. To nie ma znaczenia
David Arno
6
„że praktycznie każdy test jednostkowy musi kpić” Tak, więc? „szybkie wyszukiwanie w Google na temat kpiny ujawnia mnóstwo artykułów, które twierdzą, że„ kpina to zapach kodu ”„ są błędne .
Michael
13
@Michael Po prostu stwierdzenie „tak, więc” i uznanie, że pogląd przeciwny jest błędny, nie jest świetnym sposobem na podejście do takich kontrowersyjnych tematów. Być może napisz odpowiedź i wyjaśnij, dlaczego Twoim zdaniem drwiny powinny być wszędzie, a może dlaczego uważasz, że „mnóstwo artykułów” jest z natury niewłaściwe?
AJFaraday
6
Ponieważ nie podałeś cytatu, że „kpina to zapach kodu”, mogę tylko zgadywać, że źle interpretujesz to, co czytasz. Kpina nie jest zapachem kodu. Potrzeba użycia odbicia lub innych shenaniganów do wstrzyknięcia sobie kpin jest zapachem kodu. Trudność wyśmiewania jest odwrotnie proporcjonalna do jakości projektu interfejsu API. Jeśli potrafisz pisać proste, proste testy jednostkowe, które przekazują próbne konstruktory, to robisz to dobrze.
TKK

Odpowiedzi:

59

celem testów jednostkowych jest testowanie jednostek kodu osobno.

Martin Fowler o teście jednostkowym

O testowaniu jednostkowym często mówi się przy tworzeniu oprogramowania i jest to termin, który znałem przez cały czas pisania programów. Jednak jak większość terminologii związanej z programowaniem, jest ona bardzo źle zdefiniowana i widzę zamieszanie, które często pojawia się, gdy ludzie myślą, że jest ona ściślej zdefiniowana niż w rzeczywistości.

To, co napisał Kent Beck w Test Driven Development, na przykład

Nazywam je „testami jednostkowymi”, ale nie pasują do przyjętej definicji testów jednostkowych

Każde twierdzenie o „punkcie testów jednostkowych” będzie w dużym stopniu zależeć od tego, jaką definicję „testu jednostkowego” rozważa się.

Jeśli twoja perspektywa jest taka, że ​​twój program składa się z wielu małych jednostek, które są od siebie zależne, i jeśli ograniczasz się do stylu, który testuje każdą jednostkę w izolacji, to wiele podwójnych testów jest nieuniknionym wnioskiem.

Sprzeczna rada, którą widzisz, pochodzi od osób działających według innego zestawu założeń.

Na przykład, jeśli piszesz testy w celu wsparcia programistów podczas procesu refaktoryzacji, a podzielenie jednej jednostki na dwie jest refaktoryzacją, którą należy wesprzeć, wtedy coś musi dać. Może ten rodzaj testu wymaga innej nazwy? A może potrzebujemy innego zrozumienia „jednostki”.

Możesz porównać:

Czy test, który testuje metodę Person.calculate bez kpiny z zależności kalkulatora (biorąc pod uwagę, że kalkulator jest lekką klasą, która nie ma dostępu do „świata zewnętrznego”), można uznać za test jednostkowy?

Myślę, że to niewłaściwe pytanie; to znowu spór o etykiety , kiedy uważam, że tak naprawdę zależy nam na właściwościach .

Wprowadzając zmiany w kodzie, nie dbam o izolację testów - już wiem, że „błąd” jest gdzieś w moim bieżącym stosie niezweryfikowanych edycji. Jeśli często uruchamiam testy, ograniczam głębokość tego stosu, a znalezienie błędu jest trywialne (w skrajnym przypadku testy są uruchamiane po każdej edycji - maksymalna głębokość stosu to jedna). Ale przeprowadzenie testów nie jest celem - jest przerwą - dlatego warto ograniczyć wpływ przerwania. Jednym ze sposobów ograniczenia przerw jest zapewnienie, że testy są szybkie ( Gary Bernhardt sugeruje 300 ms , ale nie wymyśliłem, jak to zrobić w moich okolicznościach).

Jeśli wywołanie Calculator::addnie wydłuży znacząco czasu potrzebnego do uruchomienia testu (lub jakiejkolwiek innej ważnej właściwości dla tego przypadku użycia), nie zawracam sobie głowy użyciem podwójnego testu - nie zapewnia korzyści przewyższających koszty .

Zwróć uwagę na dwa założenia tutaj: istota ludzka w ramach oceny kosztów oraz krótki stos niezweryfikowanych zmian w ocenie korzyści. W okolicznościach, w których te warunki się nie utrzymują, wartość „izolacji” dość się zmienia.

Zobacz także Hot Lava , autor: Harry Percival.

VoiceOfUnreason
źródło
5
jedną z czynności dodawanych przez drwiny jest udowodnienie, że kalkulator można wyśmiewać, tzn. że projekt nie łączy osoby i kalkulatora (chociaż można to również sprawdzić na inne sposoby)
jk.
40

Jak dokładnie należy pisać testy jednostkowe bez obszernego kpienia?

Minimalizując skutki uboczne w kodzie.

Biorąc przykładowy kod, jeśli calculatorna przykład rozmawia on z internetowym interfejsem API, albo tworzysz delikatne testy, które polegają na możliwości interakcji z tym internetowym interfejsem API, albo tworzysz jego próbę. Jeśli jednak jest to deterministyczny, bezstanowy zestaw funkcji obliczeniowych, to nie (i nie powinieneś) kpić z niego. Jeśli to zrobisz, ryzykujesz, że Twój makieta zachowuje się inaczej niż prawdziwy kod, co prowadzi do błędów w testach.

Próbki powinny być potrzebne tylko w przypadku kodu, który odczytuje / zapisuje do systemu plików, baz danych, punktów końcowych adresów URL itp .; które są zależne od środowiska, w którym pracujesz; lub które mają charakter wysoce stanowy i niedeterministyczny. Jeśli więc ograniczysz te części kodu do minimum i ukryjesz je za abstrakcjami, łatwo będzie z nich wyśmiewać, a reszta kodu pozwoli uniknąć prób.

W przypadku punktów kodowych, które mają skutki uboczne, warto pisać testy, które kpią i testy, które nie. Te ostatnie wymagają jednak opieki, ponieważ z natury będą kruche i prawdopodobnie powolne. Możesz więc chcieć uruchamiać je tylko na noc na serwerze CI, a nie za każdym razem, gdy zapisujesz i budujesz swój kod. Poprzednie testy powinny być jednak przeprowadzane tak często, jak to możliwe. To, czy każdy test jest wtedy testem jednostkowym, czy testem integracyjnym, staje się akademickim i pozwala uniknąć „wojen płomieniowych” o to, co jest i nie jest testem jednostkowym.

David Arno
źródło
8
To poprawna odpowiedź zarówno w praktyce, jak i pod względem unikania bezsensownej debaty semantycznej.
Jared Smith
Czy masz przykład nietrywialnej bazy kodu o otwartym kodzie źródłowym, która używa takiego stylu i nadal ma dobre pokrycie testowe?
Joeri Sebrechts
4
@JeriSebrechts każdy pojedynczy FP? Przykład
Jared Smith
Nie do końca to, czego szukam, ponieważ to tylko zbiór funkcji, które są od siebie niezależne, a nie system funkcji, które się nawzajem wywołują. Jak poradzić sobie z konstruowaniem złożonych argumentów funkcji w celu jej przetestowania, jeśli jest to jedna z funkcji najwyższego poziomu? Np. Podstawowa pętla gry.
Joeri Sebrechts
1
@JeriSebrechts hmm, albo nie rozumiem, czego chcesz, albo nie zagłębiłeś się wystarczająco w podany przeze mnie przykład. Funkcje ramda używają wywołań wewnętrznych w dowolnym miejscu w swoim źródle (np R.equals.). Ponieważ są to w większości funkcje czysto, na ogół nie są wyśmiewane w testach.
Jared Smith
31

Te pytania różnią się pod względem trudności. Najpierw weźmy pytanie 2.

Testy jednostkowe i testy integracyjne są wyraźnie oddzielone. Test jednostkowy testuje jedną jednostkę (metodę lub klasę) i wykorzystuje inne jednostki tylko w takim stopniu, w jakim jest to konieczne do osiągnięcia tego celu. Drwiny mogą być konieczne, ale nie o to chodzi w teście. Test integracji sprawdza interakcję między różnymi rzeczywistymi jednostkami. Ta różnica jest całkowitym powodem, dla którego potrzebujemy zarówno testów jednostkowych, jak i integracyjnych - gdyby jedno dobrze wykonało drugie, nie zrobilibyśmy tego, ale okazało się, że zwykle bardziej wydajne jest użycie dwóch specjalistycznych narzędzi niż jednego ogólnego narzędzia .

Teraz ważne pytanie: jak należy przeprowadzić test jednostkowy? Jak wspomniano powyżej, testy jednostkowe powinny konstruować struktury pomocnicze tylko w miarę potrzeby . Często łatwiej jest używać próbnej bazy danych niż prawdziwej bazy danych, a nawet jakiejkolwiek prawdziwej bazy danych. Wyśmiewanie samo w sobie nie ma jednak żadnej wartości. Jeśli często się zdarza, że ​​w rzeczywistości łatwiej jest użyć rzeczywistych składników innej warstwy jako danych wejściowych do testu jednostkowego na średnim poziomie. Jeśli tak, nie wahaj się ich użyć.

Wielu praktyków obawia się, że jeśli test jednostkowy B ponownie wykorzysta klasy, które zostały już przetestowane w teście jednostkowym A, wówczas wada w jednostce A powoduje niepowodzenia testu w wielu miejscach. Uważam, że to nie problem: zestaw testowy ma odnieść sukces w 100% , aby dać Ci pewność co potrzeba, więc nie jest to duży problem, aby mieć zbyt wielu porażek - po tym wszystkim, zrobić mieć wadę. Jedynym krytycznym problemem byłby błąd, który spowodowałby zbyt małą liczbę awarii.

Dlatego nie twórzcie religii kpiny. To środek, a nie cel, więc jeśli uda ci się uniknąć dodatkowego wysiłku, powinieneś to zrobić.

Kilian Foth
źródło
4
The only critical problem would be if a defect triggered too few failures.jest to jeden ze słabych punktów kpin. Musimy „zaprogramować” oczekiwane zachowanie, więc możemy się nie udać, powodując zakończenie testów jako „fałszywie pozytywne”. Ale kpina jest bardzo przydatną techniką w celu osiągnięcia determinizmu (najważniejszego warunku testowania). Używam ich we wszystkich moich projektach, jeśli to możliwe. Pokazują też, kiedy integracja jest zbyt złożona lub zależność zbyt ścisła.
Laiv
1
Jeśli testowana jednostka korzysta z innych jednostek, czy tak naprawdę nie jest to test integracyjny? Ponieważ w gruncie rzeczy ta jednostka testowałaby interakcję między wymienionymi jednostkami, dokładnie tak jak test integracji.
Alexander Lomia
11
@AlexanderLomia: Jak nazwałbyś jednostkę? Czy nazwałbyś też „String” jednostką? Zrobiłbym to, ale nie śniło mi się z tego kpić.
Bart van Ingen Schenau
5
Testy jednostkowe i testy integracyjne są wyraźnie oddzielone. Test jednostkowy testuje jedną jednostkę (metodę lub klasę) i wykorzystuje inne jednostki tylko w takim stopniu, w jakim jest to konieczne do osiągnięcia tego celu ”. Oto pocieranie. To twoja definicja testu jednostkowego. Mój jest zupełnie inny. Tak więc rozróżnienie między nimi jest tylko „wyraźnie oddzielone” dla każdej danej definicji, ale rozdział jest różny dla różnych definicji.
David Arno
4
@ Voo Pracowałem z takimi bazami kodów, chociaż znalezienie oryginalnego problemu może być uciążliwe (szczególnie jeśli architektura pomalowała rzeczy, których użyjesz do debugowania), wciąż mam więcej problemów z próbami, które spowodowały testy działające po zerwaniu kodu, którego użyto do testowania.
James_pic
6

OK, aby bezpośrednio odpowiedzieć na pytania:

Jak należy poprawnie pisać testy jednostkowe?

Jak mówisz, powinieneś kpić z zależności i testować tylko daną jednostkę.

Gdzie dokładnie leży granica między nimi a testami integracyjnymi?

Test integracji to test jednostkowy, w którym zależności nie są wyśmiewane.

Czy test, który testuje metodę Person.calculate bez wyśmiewania się z kalkulatora, można uznać za test jednostkowy?

Nie. Musisz wprowadzić zależność kalkulatora do tego kodu i masz wybór między wersją próbną a rzeczywistą. Jeśli użyjesz fałszywego, jest to test jednostkowy, jeśli użyjesz prawdziwego, to test integracyjny.

Jednak zastrzeżenie. czy naprawdę obchodzi Cię, jak ludzie myślą, że twoje testy powinny się nazywać?

Ale twoje prawdziwe pytanie wydaje się następujące:

szybkie wyszukiwanie Google na temat kpiny ujawnia mnóstwo artykułów, które twierdzą, że „kpina to zapach kodu” i należy go w większości (choć nie całkowicie) unikać.

Myślę, że problem polega na tym, że wiele osób używa próbnych elementów do pełnego odtworzenia zależności. Na przykład mogę wyśmiewać kalkulator w twoim przykładzie jako

public class MockCalc : ICalculator
{
     public Add(int a, int b) { return 4; }
}

Nie zrobiłbym czegoś takiego:

myMock = Mock<ICalculator>().Add((a,b) => {return a + b;})
myPerson.Calculate()
Assert.WasCalled(myMock.Add());

Twierdziłbym, że byłoby to „testowanie mojej makiety” lub „testowanie implementacji”. Powiedziałbym: „ Nie pisz Mocków! * W ten sposób”.

Inni ludzie by się ze mną nie zgodzili, zaczęlibyśmy na naszych blogach masowe wojny płomieni o najlepszy sposób na udawanie, co naprawdę nie miałoby sensu, chyba że zrozumiałbyś całe tło różnych podejść i naprawdę nie oferowałbyś zbyt dużej wartości do kogoś, kto chce po prostu pisać dobre testy.

Ewan
źródło
Dzięki za wyczerpującą odpowiedź. W trosce o to, co inni ludzie myślą o moich testach - tak naprawdę chcę uniknąć pisania testów półintegracyjnych, półjednostkowych, które stają się nierzetelnie bałaganem w miarę postępu projektu.
Alexander Lomia
żadnych probów, myślę, że problem polega na tym, że definicje tych dwóch rzeczy nie są w 100% uzgodnione przez wszystkich.
Ewan
Zmieniłbym nazwę twojej klasy MockCalcna StubCalci nazwałbym to skrótem, a nie próbą. martinfowler.com/articles/…
bdsl
@bdsl Ten artykuł ma 15 lat
Ewan
4
  1. Jak prawidłowo przeprowadzać testy jednostkowe?

Moja ogólna zasada polega na tym, że właściwe testy jednostkowe:

  • Są kodowane przeciwko interfejsom, a nie implementacjom . Ma to wiele zalet. Po pierwsze, zapewnia, że ​​twoje klasy będą postępować zgodnie z zasadą inwersji zależności od SOLID . Tak też robią inne klasy ( prawda? ), Więc testy powinny robić to samo. Pozwala to również na przetestowanie wielu implementacji tego samego interfejsu przy jednoczesnym ponownym wykorzystaniu dużej części kodu testowego (zmieniłaby się tylko inicjalizacja i niektóre twierdzenia).
  • Są samodzielne . Jak powiedziałeś, zmiany w dowolnym kodzie zewnętrznym nie mogą wpływać na wynik testu. Jako takie, testy jednostkowe mogą być wykonywane w czasie kompilacji. Oznacza to, że potrzebujesz makiet, aby usunąć wszelkie skutki uboczne. Jeśli jednak postępujesz zgodnie z zasadą inwersji zależności, powinno to być względnie łatwe. Dobre frameworki testowe, takie jak Spock, mogą być używane do dynamicznego dostarczania próbnych implementacji dowolnego interfejsu do użycia w testach przy minimalnym kodowaniu. Oznacza to, że każda klasa testowa musi tylko wykonać kod z dokładnie jednej klasy implementacyjnej, plus szkielet testowy (i być może klasy modelowe [„bean”]).
  • Nie wymaga osobnej działającej aplikacji . Jeśli test musi „rozmawiać z czymś”, bez względu na to, czy jest to baza danych, czy usługa internetowa, jest to test integracyjny, a nie test jednostkowy. Rysuję linię na połączeniach sieciowych lub systemie plików. Na przykład czysta pamięć SQLite na bazie danych jest, moim zdaniem, uczciwą grą dla testu jednostkowego, jeśli naprawdę tego potrzebujesz.

Jeśli istnieją klasy narzędzi z frameworków, które komplikują testowanie jednostkowe, może nawet okazać się przydatne tworzenie bardzo prostych interfejsów i klas „otokowych” w celu ułatwienia kpienia z tych zależności. Owijarki te niekoniecznie podlegałyby testom jednostkowym.

  1. Gdzie dokładnie leży granica między nimi [testy jednostkowe] i testy integracyjne?

Uważam, że to rozróżnienie jest najbardziej przydatne:

  • Testy jednostkowe symulują „kod użytkownika” , weryfikując zachowanie klas implementacyjnych względem pożądanego zachowania i semantyki interfejsów na poziomie kodu .
  • Testy integracyjne symulują użytkownika , weryfikując zachowanie działającej aplikacji względem określonych przypadków użycia i / lub formalnych interfejsów API. W przypadku usługi internetowej „użytkownik” byłby aplikacją kliencką.

Jest tu szara strefa. Na przykład, jeśli możesz uruchomić aplikację w kontenerze Docker i uruchomić testy integracyjne jako ostatni etap kompilacji, a następnie zniszczyć kontener, to czy można uwzględnić te testy jako „testy jednostkowe”? Jeśli to jest twoja płonąca debata, jesteś w całkiem dobrym miejscu.

  1. Czy to prawda, że ​​praktycznie każdy test jednostkowy musi kpić?

Nie. Niektóre indywidualne przypadki testowe dotyczą warunków błędu, takich jak przekazanie nulljako parametr i sprawdzenie, czy otrzymano wyjątek. Wiele takich testów nie będzie wymagać żadnych prób. Ponadto implementacje, które nie mają skutków ubocznych, na przykład przetwarzanie ciągów lub funkcje matematyczne, mogą nie wymagać żadnych prób, ponieważ po prostu weryfikują dane wyjściowe. Ale myślę, że większość klas, które warto mieć, będzie wymagać co najmniej jednej makiety w kodzie testowym. (Im mniej, tym lepiej.)

Wspomniany problem „zapachu kodu” powstaje, gdy masz zbyt skomplikowaną klasę, która wymaga długiej listy próbnych zależności w celu napisania testów. Jest to wskazówka, że ​​musisz zmienić implementację i podzielić rzeczy, aby każda klasa miała mniejszy ślad i wyraźniejszą odpowiedzialność, a zatem łatwiej ją przetestować. Poprawi to jakość na dłuższą metę.

Tylko jeden test jednostkowy powinien przełamać błąd w testowanej jednostce.

Nie sądzę, że jest to uzasadnione oczekiwanie, ponieważ działa przeciwko ponownemu użyciu. Możesz mieć privatena przykład metodę, która jest wywoływana przez wiele publicmetod opublikowanych przez twój interfejs. Błąd wprowadzony do tej jednej metody może spowodować wiele niepowodzeń testu. Nie oznacza to, że powinieneś skopiować ten sam kod do każdej publicmetody.

jagoda
źródło
3
  1. Nie powinny łamać się przez jakąkolwiek niepowiązaną zmianę kodu w innym miejscu w bazie kodu.

Nie jestem do końca pewien, w jaki sposób ta zasada jest przydatna. Jeśli zmiana w jednej klasie / metodzie / czymkolwiek może zepsuć zachowanie innej w kodzie produkcyjnym, wówczas rzeczy są w rzeczywistości współpracownikami i nie są ze sobą niezwiązane. Jeśli testy się zepsują, a kod produkcyjny się nie powiedzie, to są podejrzane.

  1. Tylko jeden test jednostkowy powinien przełamać błąd w testowanej jednostce, w przeciwieństwie do testów integracyjnych (które mogą pęknąć w stosach).

Uważam tę zasadę również za podejrzaną. Jeśli jesteś naprawdę wystarczająco dobry, aby ustrukturyzować swój kod i napisać testy tak, że jeden błąd powoduje dokładnie jeden błąd testu jednostkowego, to znaczy, że zidentyfikowałeś już wszystkie potencjalne błędy, nawet gdy baza kodów ewoluuje, aby używać przypadków, w których nie przewidziałem.

Gdzie dokładnie leży granica między nimi a testami integracyjnymi?

Nie sądzę, żeby to było ważne rozróżnienie. Czym w ogóle jest „jednostka” kodu?

Spróbuj znaleźć punkty wejścia, w których możesz pisać testy, które „mają sens” pod względem problematycznej domeny / reguł biznesowych, z którymi ma do czynienia ten poziom kodu. Często testy te mają charakter „funkcjonalny” - są wprowadzane do wejścia i sprawdzają, czy dane wyjściowe są zgodne z oczekiwaniami. Jeśli testy wyrażają pożądane zachowanie systemu, wówczas często pozostają dość stabilne, nawet jeśli kod produkcyjny ewoluuje i jest refaktoryzowany.

Jak dokładnie należy pisać testy jednostkowe bez obszernego kpienia?

Nie odczytuj za dużo słowa „jednostka” i skłaniaj się do używania prawdziwych klas produkcyjnych w testach, nie martwiąc się zbytnio, jeśli angażujesz więcej niż jedną z nich w test. Jeśli jeden z nich jest trudny w użyciu (ponieważ wymaga dużo inicjalizacji lub musi trafić do prawdziwej bazy danych / serwera e-mail itp.), Pozwól swoim myślom przejść do drwiny / fałszowania.

Topo Morto
źródło
Co to jest„ jednostka ”kodu? ” Bardzo dobre pytanie, które może mieć nieoczekiwane odpowiedzi, które mogą nawet zależeć od tego, kto odpowiada. Zazwyczaj większość definicji testów jednostkowych wyjaśnia je jako odnoszące się do metody lub klasy, ale nie jest to naprawdę przydatna miara „jednostki” we wszystkich przypadkach. Jeśli mam Person:tellStory()metodę, która łączy dane osoby w ciąg znaków, zwraca to, a następnie „historia” to prawdopodobnie jedna jednostka. Jeśli stworzę metodę prywatnego pomocnika, która ukrywa część kodu, nie sądzę, że wprowadziłem nową jednostkę - nie muszę tego testować osobno.
VLAZ,
1

Po pierwsze, niektóre definicje:

Test jednostkowy testuje jednostki w oderwaniu od innych jednostek, ale to, co to oznacza, nie jest konkretnie zdefiniowane przez żadne wiarygodne źródło, więc zdefiniujmy to nieco lepiej: jeśli granice We / Wy zostaną przekroczone (czy to We / Wy to sieć, dysk, ekran lub wejście interfejsu użytkownika), jest pół-obiektywne miejsce, w którym możemy narysować linię. Jeśli kod zależy od operacji we / wy, przekracza granicę jednostki i dlatego będzie musiał kpić z jednostki odpowiedzialnej za to operacje we / wy.

Zgodnie z tą definicją nie widzę przekonującego powodu, aby wyśmiewać takie rzeczy jak czyste funkcje, co oznacza, że ​​testowanie jednostkowe nadaje się do czystych funkcji lub funkcji bez skutków ubocznych.

Jeśli chcesz testować jednostki z efektami, jednostki odpowiedzialne za efekty powinny być wyśmiewane, ale być może powinieneś rozważyć test integracji. Tak więc krótka odpowiedź brzmi: „jeśli chcesz kpić, zadaj sobie pytanie, czy tak naprawdę potrzebujesz testu integracji”. Ale jest lepsza, dłuższa odpowiedź tutaj, a królicza nora idzie znacznie głębiej. Makiety mogą być moim ulubionym zapachem kodu, ponieważ można się z nich wiele nauczyć.

Zapach kodu

W tym celu przejdziemy do Wikipedii:

W programowaniu komputerowym zapach kodu jest dowolną cechą w kodzie źródłowym programu, która może wskazywać na głębszy problem.

Kontynuuje później ...

„Zapachy to niektóre struktury w kodzie, które wskazują na naruszenie podstawowych zasad projektowania i negatywnie wpływają na jakość projektu”. Suryanarayana, Girish (listopad 2014). Refaktoryzacja dla zapachów projektowania oprogramowania. Morgan Kaufmann. p. 258

Zapachy kodowe zwykle nie są błędami; nie są technicznie niepoprawne i nie uniemożliwiają funkcjonowania programu. Zamiast tego wskazują na słabości w projekcie, które mogą spowolnić rozwój lub zwiększyć ryzyko błędów lub awarii w przyszłości.

Innymi słowy, nie wszystkie zapachy kodowe są złe. Zamiast tego są powszechnymi wskazaniami, że coś może nie być wyrażone w optymalnej formie, a zapach może wskazywać na możliwość poprawienia danego kodu.

W przypadku kpiny zapach wskazuje, że jednostki, które wydają się wołać o kpiny, zależą od jednostek, które mają być kpione. Może to wskazywać, że nie rozłożyliśmy problemu na części, które można rozwiązać za pomocą atomów, a to może wskazywać na wadę projektową oprogramowania.

Istotą całego rozwoju oprogramowania jest proces dzielenia dużego problemu na mniejsze, niezależne części (rozkład) i komponowanie rozwiązań w celu utworzenia aplikacji, która rozwiązuje duży problem (kompozycję).

Wyśmiewanie jest wymagane, gdy jednostki użyte do rozbicia dużego problemu na mniejsze części zależą od siebie. Innymi słowy, kpina jest wymagana, gdy nasze domniemane atomowe jednostki składu nie są tak naprawdę atomowe, a nasza strategia rozkładu nie zdołała rozłożyć większego problemu na mniejsze, niezależne problemy do rozwiązania.

To, co powoduje, że kpiny pachną kodem, nie polega na tym, że z mockiem nie ma nic złego - czasem jest to bardzo przydatne. To, co powoduje, że kod ma zapach, może wskazywać na problematyczne źródło sprzężenia w Twojej aplikacji. Czasami usunięcie tego źródła sprzężenia jest o wiele bardziej produktywne niż pisanie próbnego.

Istnieje wiele rodzajów sprzężenia, a niektóre są lepsze od innych. Zrozumienie, że makiety są zapachem kodu, może nauczyć cię rozpoznawania i unikania najgorszych rodzajów na wczesnym etapie cyklu życia aplikacji, zanim zapach zmieni się w coś gorszego.

Eric Elliott
źródło
0

Wyśmiewanie powinno być stosowane tylko w ostateczności, nawet w testach jednostkowych.

Metoda nie jest jednostką, a nawet klasa nie jest jednostką. Jednostka to logiczne oddzielenie kodu, które ma sens, niezależnie od tego, jak go nazwiesz. Ważnym elementem dobrze przetestowanego kodu jest możliwość swobodnego refaktoryzacji, a część możliwości swobodnego refaktoryzacji oznacza, że ​​nie musisz zmieniać swoich testów, aby to zrobić. Im więcej kpisz, tym bardziej musisz zmieniać testy podczas refaktoryzacji. Jeśli uważasz, że metoda jest jednostką, musisz zmieniać testy za każdym razem, gdy dokonujesz refaktoryzacji. A jeśli uważasz klasę za jednostkę, musisz zmieniać testy za każdym razem, gdy chcesz podzielić klasę na kilka klas. Kiedy trzeba przefakturować swoje testy, aby przefaktoryzować kod, ludzie decydują się nie refaktoryzować swojego kodu, co jest najgorszą rzeczą, jaka może się przydarzyć projektowi. Bardzo ważne jest, abyś mógł podzielić klasę na wiele klas bez konieczności ponownego testowania swoich testów, w przeciwnym razie skończysz na ponad 500 liniowych klasach spaghetti. Jeśli traktujesz metody lub klasy jako swoje jednostki z testowaniem jednostkowym, prawdopodobnie nie robisz programowania obiektowego, ale jakiegoś zmutowanego programowania funkcjonalnego z obiektami.

Wyodrębnienie kodu do testu jednostkowego nie oznacza, że ​​wyśmiewasz wszystko poza nim. Gdyby tak było, musiałbyś kpić z lekcji matematyki w swoim języku i absolutnie nikt nie uważa, że ​​to dobry pomysł. Zależności wewnętrzne nie powinny być traktowane inaczej niż zależności zewnętrzne. Ufasz, że są dobrze przetestowane i działają tak, jak powinny. Jedyną prawdziwą różnicą jest to, że jeśli wewnętrzne zależności psują moduły, możesz przerwać to, co robisz, aby to naprawić, zamiast publikować problem na GitHub i albo zagłębić się w bazę kodu, której nie rozumiesz, aby to naprawić. lub mam nadzieję na najlepsze.

Izolowanie kodu oznacza po prostu, że traktujesz swoje wewnętrzne zależności jak czarne skrzynki i nie testujesz rzeczy, które się w nich zachodzą. Jeśli masz moduł B, który akceptuje dane wejściowe 1, 2 lub 3, i masz moduł A, który go wywołuje, nie masz testów dla modułu A dla każdej z tych opcji, po prostu wybierz jedną z nich i użyj jej. Oznacza to, że twoje testy dla modułu A powinny przetestować różne sposoby traktowania odpowiedzi z modułu B, a nie rzeczy, które do niego przechodzisz.

Tak więc, jeśli kontroler przekazuje złożony obiekt do zależności, a ta zależność robi kilka możliwych rzeczy, być może zapisując ją do bazy danych i może zwracając różne błędy, ale tak naprawdę wszystko, co robi, to po prostu sprawdzić, czy zwraca błąd lub nie i przekaż te informacje razem, wtedy wszystko, co testujesz w kontrolerze, to jeden test na to, czy zwraca błąd i przekazuje go wraz z drugim testem, jeśli nie zwraca błędu. Nie testujesz, czy coś zostało zapisane w bazie danych lub jakiego rodzaju błąd to błąd, ponieważ byłby to test integracyjny. Aby to zrobić, nie musisz kpić z zależności. Wyizolowałeś kod.

TiggerToo
źródło