Jak wykrywasz problemy z zależnościami w testach jednostkowych, gdy używasz próbnych obiektów?

98

Masz klasę X i piszesz testy jednostkowe, które weryfikują zachowanie X1. Istnieje również klasa A, która bierze X jako zależność.

Pisząc testy jednostkowe dla A, kpisz z X. Innymi słowy, podczas testowania jednostkowego A ustawiasz (postulujesz) zachowanie próbnego X na X1. Czas płynie, ludzie używają twojego systemu, potrzebuje zmian, X ewoluuje: modyfikujesz X, aby pokazać zachowanie X2. Oczywiście testy jednostkowe dla X nie powiodą się i będziesz musiał je dostosować.

Ale co z A? Testy jednostkowe dla A nie zakończą się niepowodzeniem, gdy zachowanie X zostanie zmodyfikowane (z powodu drwiny z X). Jak wykryć, że wynik A będzie różny, gdy zostanie uruchomiony z „prawdziwym” (zmodyfikowanym) X?

Oczekuję odpowiedzi w stylu: „To nie jest cel testowania jednostkowego”, ale jaką wartość ma testowanie jednostkowe? Czy to naprawdę mówi ci tylko, że kiedy wszystkie testy zakończą się pomyślnie, nie wprowadziłeś przełomowej zmiany? A gdy zachowanie niektórych klas zmienia się (dobrowolnie lub niechętnie), jak możesz (najlepiej w sposób zautomatyzowany) wykryć wszystkie konsekwencje? Czy nie powinniśmy koncentrować się bardziej na testach integracyjnych?

bvgheluwe
źródło
36
Oprócz wszystkich sugerowanych odpowiedzi muszę powiedzieć, że mam problem z następującym stwierdzeniem: „Czy to naprawdę mówi tylko , że kiedy wszystkie testy zakończą się pomyślnie, nie wprowadziłeś przełomowej zmiany?” Jeśli naprawdę uważasz, że usunięcie strachu przed refaktoryzacją ma niewielką wartość, jesteś na dobrej drodze do napisania
niemożliwego do utrzymania
5
Testowanie jednostkowe informuje, czy jednostka kodu zachowuje się zgodnie z oczekiwaniami. Nie więcej ani mniej. Egzaminy i duplikaty testowe zapewniają sztuczne, kontrolowane środowisko do ćwiczenia jednostki kodu (w izolacji), aby sprawdzić, czy spełnia ona Twoje oczekiwania. Nie więcej ani mniej.
Robert Harvey
2
Uważam, że twoje założenie jest nieprawidłowe. Kiedy wspominasz, X1mówisz, że Ximplementuje interfejs X1. Jeśli zmienić interfejs X1do X2makiety użytego w innych badaniach nie powinna już kompilacji, stąd jesteś zmuszony rozwiązać te testy też. Zmiany w zachowaniu klasy nie powinny mieć znaczenia. W rzeczywistości twoja klasa Anie powinna zależeć od szczegółów implementacji (co w takim przypadku miałbyś zmienić). Testy jednostkowe Asą więc nadal poprawne i mówią, że Adziała, biorąc pod uwagę idealną implementację interfejsu.
Bakuriu
5
Nie wiem o tobie, ale kiedy muszę pracować na bazie kodu bez testów, boję się śmierci, coś zepsuję. I dlaczego? Ponieważ zdarza się tak często, że coś pęka, gdy nie jest przeznaczone. I pobłogosław serca naszych testerów, nie mogą wszystkiego przetestować. Lub nawet blisko. Ale test jednostkowy z radością wypiera się przez nudną rutynę po nudnej rutynie.
corsiKa

Odpowiedzi:

125

Kiedy piszesz testy jednostkowe dla A, wyśmiewasz X

Czy ty? Ja nie, chyba że absolutnie muszę. Muszę, jeśli:

  1. X jest wolny lub
  2. X ma skutki uboczne

Jeśli żadne z nich nie ma zastosowania, moje testy jednostkowe również Azostaną przetestowane X. Robienie czegokolwiek innego oznaczałoby doprowadzenie testów izolujących do nielogicznej skrajności.

Jeśli masz części kodu za pomocą próbnych fragmentów kodu, zgodzę się: jaki jest sens takich testów jednostkowych? Więc nie rób tego. Pozwól, aby te testy wykorzystywały rzeczywiste zależności, ponieważ w ten sposób tworzą o wiele bardziej wartościowe testy.

A jeśli niektórzy ludzie denerwują się tym, że nazywasz te testy „testami jednostkowymi”, po prostu nazywaj je „testami automatycznymi” i zacznij pisać dobre testy automatyczne.

David Arno
źródło
94
@Laiv, Nie, testy jednostkowe powinny działać jako jednostki, tj. Działać w oderwaniu od innych testów . Węzły i wykresy mogą wędrować. Jeśli uda mi się w krótkim czasie przeprowadzić izolowany, wolny od efektów kompleksowy test, to jest to test jednostkowy. Jeśli nie podoba ci się ta definicja, nazwij ją testem automatycznym i przestań pisać testy bzdur, aby dopasować głupią semantykę.
David Arno
9
@DavidArno Istnieje niestety bardzo szeroka definicja izolacji. Niektórzy chcieliby, aby „jednostka” obejmowała warstwę pośrednią i bazę danych. Mogą wierzyć, co im się podoba, ale są szanse, że przy rozwoju dowolnej wielkości koła odpadną w dość krótkim czasie, ponieważ kierownik budowy je wyrzuci. Zasadniczo, jeśli są izolowane do zestawu (lub odpowiednika), nie ma problemu. Uwaga: jeśli kodujesz do interfejsów, o wiele łatwiej jest później dodać kpiny i DI.
Robbie Dee
13
Opowiadasz się za innym rodzajem testu, niż odpowiadasz na pytanie. Co jest słusznym punktem, ale jest to dość niedokładny sposób na zrobienie tego.
Phil Frost
17
@PhilFrost, cytując siebie: „ A jeśli niektórzy ludzie denerwują się tym, że wywołujesz te testy,„ testy jednostkowe ”, po prostu nazywaj je„ testami automatycznymi ”i zacznij pisać dobre testy automatyczne. ” Napisz przydatne testy, a nie głupie testy które spełniają po prostu losową definicję słowa. Ewentualnie zaakceptuj, że być może masz błędną definicję „testu jednostkowego” i że zbyt często używasz próbnych testów, ponieważ źle ją popełniłeś. Tak czy inaczej, skończysz z lepszymi testami.
David Arno
30
W tej sprawie jestem z @DavidArno; moja strategia testowania zmieniła się po obejrzeniu tego wystąpienia Iana Coopera: vimeo.com/68375232 . Sprowadzając to do jednego zdania: Nie testuj zajęć . Testuj zachowania . Twoje testy nie powinny mieć wiedzy na temat wewnętrznych klas / metod wykorzystywanych do wdrożenia pożądanego zachowania; powinni znać tylko publiczną powierzchnię twojego API / biblioteki i powinni to przetestować. Jeśli testy mają zbyt dużą wiedzę, oznacza to, że testujesz szczegóły implementacji, a testy stają się kruche, w połączeniu z implementacją, a właściwie tylko kotwicą na szyi.
Richiban
79

Potrzebujesz obu. Testy jednostkowe w celu zweryfikowania zachowania każdej z jednostek oraz kilka testów integracyjnych w celu upewnienia się, że są one prawidłowo połączone. Problem polegający na poleganiu wyłącznie na testach integracyjnych polega na eksplozji kombinatorycznej wynikającej z interakcji między wszystkimi jednostkami.

Załóżmy, że masz klasę A, która wymaga 10 testów jednostkowych, aby w pełni pokryć wszystkie ścieżki. Następnie masz inną klasę B, która również wymaga 10 testów jednostkowych, aby objąć wszystkie ścieżki, które kod może przez nią przejść. Powiedzmy teraz, że w twojej aplikacji musisz podać dane wyjściowe A do B. Teraz twój kod może pobierać 100 różnych ścieżek od danych wejściowych A do danych wyjściowych B.

W przypadku testów jednostkowych potrzebujesz tylko 20 testów jednostkowych + 1 test integracyjny, aby całkowicie objąć wszystkie przypadki.

Dzięki testom integracyjnym potrzebujesz 100 testów, aby objąć wszystkie ścieżki kodu.

Oto bardzo dobre wideo na temat wad polegających na testach integracyjnych. Tylko zintegrowane testy JB Rainsberger Are A Scam HD

Eternal21
źródło
1
Jestem pewien, że to nie przypadek, że znak zapytania dotyczący skuteczności testów integracyjnych szedł w parze z testami jednostkowymi obejmującymi kolejne warstwy.
Robbie Dee
16
Zgadza się, ale nigdzie twój test jednostkowy nie wymaga kpin. Jeśli masz 10 testów A obejmujących wszystkie A i 10 testów obejmujących wszystkie B, a także ponownie przetestujesz 25% A jako premię, wydaje się, że jest to „dobre” i dobre. Wyśmiewanie A w testach Bs wydaje się aktywnie głupie (chyba że naprawdę istnieje powód, dla którego A jest problemem, np. Baza danych lub wprowadza długą sieć innych rzeczy)
Richard Tingle
9
Nie zgadzam się z pomysłem, że pojedynczy test integracji jest wystarczający, jeśli chcesz uzyskać pełne pokrycie. Reakcje B na to, co wyjściowe A będą się różnić w zależności od wyniku; jeśli zmiana parametru w A zmienia jego wynik, wówczas B może nie obsługiwać go poprawnie.
Matthieu M.
3
@ Eternal21: Chodziło mi o to, że czasami problem nie polega na indywidualnym zachowaniu, ale na nieoczekiwanej interakcji. Oznacza to, że klej między A i B zachowuje się nieoczekiwanie w niektórych scenariuszach. Tak więc A i B działają zgodnie ze specyfikacjami, a szczęśliwy przypadek działa, ale na niektórych wejściach jest błąd w kodzie kleju ...
Matthieu M.
1
@MatthieuM. Twierdzę, że wykracza to poza zakres testów jednostkowych. Kod kleju można przetestować samodzielnie, podczas gdy interakcje między A i B za pomocą kodu kleju są testem integracji. Po znalezieniu określonych przypadków krawędzi lub błędów można je dodać do testów jednostek kodu kleju i ostatecznie zweryfikować w testach integracji.
Andrew T Finnell,
72

Pisząc testy jednostkowe dla A, kpisz z X. Innymi słowy, podczas testowania jednostkowego A ustawiasz (postulujesz) zachowanie próbnego X na X1. Czas płynie, ludzie używają twojego systemu, potrzebuje zmian, X ewoluuje: modyfikujesz X, aby pokazać zachowanie X2. Oczywiście testy jednostkowe dla X nie powiodą się i będziesz musiał je dostosować.

Łał, poczekaj chwilę. Implikacje testów dla niepowodzenia X są zbyt ważne, aby można je było tak przefiltrować.

Jeśli zmiana implementacji X z X1 na X2 psuje testy jednostkowe dla X, oznacza to, że dokonałeś wstecz niezgodnej zmiany w kontrakcie X.

X2 nie jest X, w sensie Liskowa , więc powinieneś pomyśleć o innych sposobach zaspokojenia potrzeb swoich interesariuszy (np. Wprowadzenie nowej specyfikacji Y, która jest implementowana przez X2).

Aby uzyskać głębsze informacje, zobacz Pieter Hinjens: The End of Software Versions lub Rich Hickey Simple Made Easy .

Z punktu widzenia A istnieje warunek, że współpracownik przestrzega kontraktu X. Twoja obserwacja jest efektywna, że ​​izolowany test dla A nie daje żadnej pewności, że A rozpoznaje współpracowników, którzy naruszają umowę X.

Recenzja zintegrowanych testów to oszustwo ; na wysokim poziomie oczekuje się, że będziesz mieć tyle izolowanych testów, ile potrzebujesz, aby upewnić się, że X2 poprawnie implementuje kontrakt X, oraz tyle izolowanych testów, ile potrzebujesz, aby upewnić się, że A robi właściwą rzecz, biorąc pod uwagę interesujące odpowiedzi od X, i pewna mniejsza liczba zintegrowanych testów, aby upewnić się, że X2 i A zgadzają się co do tego, co oznacza X.

Czasami zobaczysz to rozróżnienie wyrażone jako samotne testy kontra sociabletesty; patrz Jay Fields współpracujący skutecznie z testami jednostkowymi .

Czy nie powinniśmy koncentrować się bardziej na testach integracyjnych?

Ponownie zobacz, jak zintegrowane testy to oszustwo - Rainsberger szczegółowo opisuje pozytywną pętlę sprzężenia zwrotnego, która jest powszechna (w jego doświadczeniach) w projektach opartych na zintegrowanych testach (pisownia notatek). Podsumowując, bez izolowanych / pojedynczych testów wywierających presję na projekt , jakość pogarsza się, prowadząc do większej liczby błędów i bardziej zintegrowanych testów ...

Będziesz także potrzebował (niektórych) testów integracyjnych. Oprócz złożoności wprowadzonej przez wiele modułów, wykonywanie tych testów ma zwykle większy opór niż testy pojedyncze; bardziej efektywne jest iterowanie bardzo szybkich kontroli, gdy praca jest w toku, zapisywanie dodatkowych kontroli, gdy uważasz, że jesteś „skończony”.

VoiceOfUnreason
źródło
8
To powinna być zaakceptowana odpowiedź. Pytanie przedstawia sytuację, w której zachowanie klasy zostało zmodyfikowane w sposób niezgodny, ale nadal wygląda na identyczne. Problem polega tutaj na zaprojektowaniu aplikacji, a nie na testach jednostkowych. Sposób, w jaki ta okoliczność zostałaby wychwycona podczas testowania, polega na zastosowaniu testu integracji między tymi dwiema klasami.
Nick Coad
1
„bez izolowanych / samotnych testów wywierających presję na projekt, jakość pogarsza się”. Myślę, że to ważna kwestia. Oprócz kontroli zachowań testy jednostkowe wywołują efekt uboczny, który zmusza do bardziej modularnej konstrukcji.
MickaëlG
Przypuszczam, że to wszystko prawda, ale jak to mi pomaga, jeśli zależność zewnętrzna wprowadza niezgodną wstecz wstecz zmianę do umowy X? Być może klasa wykonująca operacje we / wy w bibliotece psuje zgodność, a my kpimy sobie z X, ponieważ nie chcemy, aby testy jednostkowe w CI zależały od ciężkich operacji we / wy. Myślę, że OP prosi o przetestowanie tego i nie rozumiem, jak to odpowiada na pytanie. Jak to przetestować?
gerrit
15

Zacznę od stwierdzenia, że ​​podstawowa przesłanka pytania jest błędna.

Nigdy nie testujesz (ani nie wyśmiewasz) implementacji, testujesz (i wyśmiewasz) interfejsy .

Jeśli mam prawdziwą klasę X, która implementuje interfejs X1, mogę napisać próbny XM, który również jest zgodny z X1. Następnie moja klasa A musi użyć czegoś, co implementuje X1, którym może być albo klasa X, albo sztuczny XM.

Załóżmy teraz, że zmienimy X, aby wprowadzić nowy interfejs X2. Oczywiście mój kod już się nie kompiluje. Wymaga czegoś, co implementuje X1 i które już nie istnieje. Problem został zidentyfikowany i można go naprawić.

Załóżmy, że zamiast zastąpić X1, po prostu go modyfikujemy. Teraz klasa A jest już ustawiona. Jednak sztuczny XM nie implementuje już interfejsu X1. Problem został zidentyfikowany i można go naprawić.


Cała podstawa do testowania jednostkowego i drwiny polega na tym, że piszesz kod korzystający z interfejsów. Konsumenta interfejsu nie obchodzi sposób implementacji kodu, tylko przestrzeganie tej samej umowy (wejścia / wyjścia).

To się psuje, gdy twoje metody mają skutki uboczne, ale myślę, że można to bezpiecznie wykluczyć, ponieważ „nie można przetestować ani wyśmiewać”.

Vlad274
źródło
11
Ta odpowiedź zawiera wiele założeń, które nie muszą się utrzymywać. Po pierwsze, z grubsza zakłada, że ​​jesteśmy w C # lub Javie (a ściślej mówiąc, że jesteśmy w skompilowanym języku, że język ma interfejsy i że X implementuje interfejs; żadne z nich nie musi być prawdziwe). Po drugie, zakłada, że ​​każda zmiana w zachowaniu lub „kontrakcie” X wymaga zmiany interfejsu (w rozumieniu kompilatora), który implementuje X. Jest to po prostu nie prawda, nawet jeśli w Java lub C #; możesz zmienić implementację metody bez zmiany jej podpisu.
Mark Amery
6
@ MarkAmery Prawdą jest, że terminologia „interfejs” jest bardziej specyficzna dla języka C # lub Java, ale myślę, że chodzi o założenie określonej „umowy” zachowania (a jeśli nie jest to skodyfikowane, automatyczne wykrycie tego jest niemożliwe). Masz również całkowitą rację, że wdrożenie można zmienić bez zmiany umowy. Ale zmiana wdrożenia bez zmiany interfejsu (lub umowy) nie powinna mieć wpływu na żadnego konsumenta. Jeśli zachowanie A zależy od sposobu implementacji interfejsu (lub kontraktu), nie można (znacząco) przeprowadzić testu jednostkowego.
Vlad274
1
„Masz również całkowitą rację, że wdrożenie można zmienić bez zmiany umowy” - chociaż to prawda, nie o to mi chodziło. Zapewniam raczej rozróżnienie między umową (rozumienie przez programistę tego, co powinien zrobić obiekt, być może określony w dokumentacji) a interfejsem (lista podpisów metod rozumianych przez kompilator) i stwierdzam, że umowa może być zmieniane bez zmiany interfejsu. Wszystkie funkcje o tej samej sygnaturze są wymienne z punktu widzenia systemu typów, ale w rzeczywistości nie!
Mark Amery
4
@MarkAmery: Nie sądzę, że Vlad używa słowa „interfejs” w tym samym znaczeniu, w jakim go używasz; w sposobie, w jaki czytam odpowiedź, nie chodzi o interfejsy w wąskim sensie C # / Java (tj. zestaw sygnatur metod), ale w ogólnym znaczeniu tego słowa, na przykład w terminach „interfejs programowania aplikacji” lub nawet „ interfejs użytkownika". [...]
Ilmari Karonen
6
@IlmariKaronen Jeśli Vlad używa „interfejsu” zamiast „wąskiego” języka C # / Java, wówczas stwierdzenie „Załóżmy, że zmienimy X, aby wdrożyć nowy interfejs X2. Oczywiście mój kod już się nie kompiluje. „ jest po prostu fałszem, ponieważ możesz zmienić kontrakt bez zmiany podpisów metod. Ale szczerze mówiąc, myślę, że problem polega na tym, że Vlad nie używa konsekwentnie żadnego znaczenia, ale je zlewa - co prowadzi do stwierdzenia, że ​​jakakolwiek zmiana w kontrakcie X1 koniecznie spowoduje błąd kompilacji, nie zauważając , że to fałsz .
Mark Amery
9

Kolejne pytania:

jaką wartość ma wówczas testowanie jednostkowe

Pisanie i uruchamianie jest tanie, a Ty otrzymujesz wczesną informację zwrotną. Jeśli złamiesz X, od razu dowiesz się, czy masz dobre testy. Nawet nie rozważ pisania testów integracyjnych, chyba że przetestowałeś wszystkie warstwy (tak, nawet w bazie danych).

Czy naprawdę mówi ci tylko, że kiedy wszystkie testy zakończą się pomyślnie, nie wprowadziłeś przełomowej zmiany

Po zdaniu testów może ci powiedzieć niewiele. Być może nie masz wystarczającej liczby testów. Być może nie przetestowałeś wystarczającej liczby scenariuszy. Pokrycie kodu może tu pomóc, ale nie jest to srebrna kula. Możesz mieć testy, które zawsze przechodzą. Stąd czerwony jest często pomijanym pierwszym krokiem czerwonego, zielonego reaktora.

A gdy zachowanie niektórych klas zmienia się (dobrowolnie lub niechętnie), w jaki sposób można wykryć (najlepiej w sposób zautomatyzowany) wszystkie konsekwencje

Więcej testów - chociaż narzędzia stają się coraz lepsze. ALE powinieneś definiować zachowanie klasy w interfejsie (patrz poniżej). Uwaga: zawsze będzie miejsce do ręcznego testowania na szczycie piramidy testowej.

Czy nie powinniśmy koncentrować się bardziej na testach integracyjnych?

Coraz więcej testów integracyjnych też nie jest odpowiedzią, są drogie w pisaniu, uruchamianiu i utrzymaniu. W zależności od konfiguracji kompilacji menedżer kompilacji może je i tak wykluczyć, czyniąc je zależnymi od programisty pamiętającego (nigdy nie jest to dobra rzecz!).

Widziałem, jak programiści spędzają godziny próbując naprawić zepsute testy integracyjne, które znaleźliby w ciągu pięciu minut, gdyby mieli dobre testy jednostkowe. Jeśli to się nie powiedzie, spróbuj po prostu uruchomić oprogramowanie - to wszystko, o co dbają Twoi użytkownicy końcowi. Nie ma sensu mieć miliona testów jednostkowych, które przejdą, jeśli cały zestaw kart upadnie, gdy użytkownik uruchomi cały pakiet.

Jeśli chcesz mieć pewność, że klasa A zużywa klasę X w ten sam sposób, powinieneś używać interfejsu zamiast konkrecji. Wtedy przełomowa zmiana jest bardziej prawdopodobna w momencie kompilacji.

Robbie Dee
źródło
9

To jest poprawne.

Testy jednostkowe mają na celu przetestowanie izolowanej funkcjonalności jednostki, sprawdzenie na pierwszy rzut oka, czy działa ona zgodnie z przeznaczeniem i nie zawiera głupich błędów.

Testy jednostkowe nie służą do sprawdzenia działania całej aplikacji.

Wiele osób zapomina, że ​​testy jednostkowe są najszybszym i najbrudniejszym sposobem sprawdzania poprawności kodu. Kiedy już wiesz, że twoje małe procedury działają, musisz także przeprowadzić testy integracji. Samo testowanie jednostkowe jest tylko nieznacznie lepsze niż brak testowania.

W ogóle mamy testy jednostkowe, ponieważ powinny być tanie. Szybkie tworzenie, uruchamianie i konserwacja. Gdy zaczniesz przekształcać je w min. Testy integracyjne, wkroczysz w świat bólu. Równie dobrze możesz przejść pełny test integracji i całkowicie zignorować testy jednostkowe, jeśli zamierzasz to zrobić.

teraz niektórzy ludzie myślą, że jednostka nie jest tylko funkcją w klasie, ale całą klasą (w tym mną). Wszystko to jednak powoduje zwiększenie rozmiaru jednostki, więc może być konieczne mniej testów integracyjnych, ale nadal jest to potrzebne. Nadal niemożliwe jest sprawdzenie, czy Twój program robi to, co powinien, bez pełnego pakietu testów integracji.

a następnie nadal musisz przeprowadzić pełne testy integracyjne w systemie na żywo (lub pół-na żywo), aby sprawdzić, czy działa on w warunkach, których używa klient.

gbjbaanb
źródło
2

Testy jednostkowe nie dowodzą poprawności czegokolwiek. Dotyczy to wszystkich testów. Zwykle testy jednostkowe są łączone z projektowaniem opartym na umowie (projekt na podstawie umowy to inny sposób, aby to powiedzieć) i ewentualnie zautomatyzowanymi dowodami poprawności, jeśli poprawność wymaga regularnej weryfikacji.

Jeśli masz rzeczywiste kontrakty, składające się z niezmienników klasy, warunków wstępnych i warunków końcowych, możliwe jest hierarchiczne udowodnienie poprawności, opierając poprawność komponentów wyższego poziomu na kontraktach komponentów niższego poziomu. To podstawowa koncepcja projektowania na podstawie umowy.

Frank Hileman
źródło
Testy jednostkowe nie dowodzą poprawności czegokolwiek . Nie jestem pewien, czy to rozumiem, czy testy jednostkowe z pewnością sprawdzają własne wyniki? A może miałeś na myśli, że zachowanie nie może być udowodnione, ponieważ może obejmować wiele warstw?
Robbie Dee,
7
@RobbieDee Chyba, że miał na myśli, kiedy testować fac(5) == 120, ty nie udowodniono, że fac()rzeczywiście powrócić silni jej argumentu. Udowodniłeś tylko, że fac()po przejściu zwraca silnię piątki 5. I nawet to nie jest pewne, jak fac()to możliwe, może wrócić 42zamiast tego w pierwsze poniedziałki po całkowitym zaćmieniu w Timbuktu ... Problem polega na tym, że nie można udowodnić zgodności poprzez sprawdzenie poszczególnych danych wejściowych testu, należy sprawdzić wszystkie możliwe dane wejściowe, a także udowodnić, że niczego nie zapomniałeś (jak czytanie zegara systemowego).
cmaster
1
Testy @RobbieDee (w tym testy jednostkowe) są złym zamiennikiem, często najlepszym dostępnym, dla prawdziwego celu, dowodem sprawdzanym przez maszynę. Rozważ całą przestrzeń stanu testowanych jednostek, w tym przestrzeń stanu dowolnych komponentów lub prób w nich zawartych. O ile nie masz bardzo ograniczonej przestrzeni stanów, testy nie mogą obejmować tej przestrzeni stanów. Pełny zasięg byłby dowodem, ale jest to dostępne tylko dla małych przestrzeni stanów, na przykład testowanie pojedynczego obiektu zawierającego pojedynczy zmienny bajt lub 16-bitową liczbę całkowitą. Zautomatyzowane proofy są znacznie bardziej wartościowe.
Frank Hileman
1
@cmaster Bardzo dobrze podsumowałeś różnicę między testem a dowodem. Dzięki!
Frank Hileman
2

Uważam, że mocno wyśmiewane testy rzadko są przydatne. W większości przypadków kończę reimplementację zachowania, które ma już klasa oryginalna, co całkowicie pokonuje cel kpin.

Dla mnie lepszą strategią jest dobre rozdzielenie problemów (np. Możesz przetestować Część A swojej aplikacji bez wprowadzania części B do Z). Tak dobra architektura naprawdę pomaga napisać dobry test.

Ponadto jestem bardziej niż chętny do zaakceptowania efektów ubocznych, o ile mogę je wycofać, np. Jeśli moja metoda modyfikuje dane w db, pozwól mu! Tak długo, jak mogę przywrócić db do poprzedniego stanu, jaka szkoda? Zaletą mojego testu jest sprawdzenie, czy dane wyglądają zgodnie z oczekiwaniami. DB w pamięci lub określone testowe wersje dbs naprawdę tu pomagają (np. Wersja testowa RavenDB w pamięci).

Wreszcie lubię robić drwiny na granicach usługi, np. Nie wykonuj tego połączenia HTTP z usługą b, ale przechwyćmy to i wprowadźmy odpowiednie

Christian Sauer
źródło
1

Chciałbym, aby ludzie w obu obozach zrozumieli, że testy klasowe i testy zachowania nie są ortogonalne.

Testy klasowe i testy jednostkowe są stosowane zamiennie i być może nie powinny. Niektóre testy jednostkowe są po prostu zaimplementowane w klasach. To wszystko. Testy jednostkowe odbywały się od dziesięcioleci w językach bez zajęć.

Jeśli chodzi o testowanie zachowań, można to zrobić w ramach testów klasowych przy użyciu konstruktu GWT.

Ponadto to, czy zautomatyzowane testy przebiegają zgodnie z klasą czy zachowaniem, zależy raczej od twoich priorytetów. Niektórzy będą musieli szybko prototypować i wyciągnąć coś za drzwi, podczas gdy inni będą mieli ograniczenia zasięgu z powodu stylów domowych. Wiele powodów. Oba są całkowicie poprawnymi podejściami. Płacisz pieniądze, wybierasz.

Co więc zrobić, gdy kod się zepsuje. Jeśli został zakodowany w interfejsie, tylko konkrecja musi się zmienić (wraz z wszelkimi testami).

Jednak wprowadzenie nowego zachowania wcale nie musi narażać systemu na szwank. Linux i in. Są pełne przestarzałych funkcji. Rzeczy takie jak konstruktory (i metody) mogą być przeciążone bez zmuszania całego kodu wywołującego do zmiany.

Tam, gdzie wygrywa testowanie klas, musisz wprowadzić zmiany w klasie, w której jeszcze nie było kanalizacji (z powodu ograniczeń czasowych, złożoności itp.). O wiele łatwiej jest zacząć z klasą, jeśli ma kompleksowe testy.

Pies Belgijski
źródło
Jeśli masz konkrecję , napisałbym implementację . Czy to jest calque z innego języka (zgaduję, że francuski), czy też jest ważne rozróżnienie między konkrecją a wdrażaniem ?
Peter Taylor
0

O ile interfejs X nie zostanie zmieniony, nie trzeba zmieniać testu jednostkowego dla A, ponieważ nic się nie zmieniło dla A. Wygląda na to, że naprawdę napisałeś razem test jednostkowy X i A, ale nazwałeś to testem jednostkowym A:

Pisząc testy jednostkowe dla A, kpisz z X. Innymi słowy, podczas testowania jednostkowego A ustawiasz (postulujesz) zachowanie próbnego X na X1.

Idealnie, próbka X powinna symulować wszystkie możliwe zachowania X, a nie tylko zachowanie oczekiwane od X. Więc bez względu na to, co faktycznie zaimplementujesz w X, A powinien już być w stanie sobie z tym poradzić. Zatem żadna zmiana na X, oprócz zmiany samego interfejsu, nie będzie miała żadnego wpływu na test jednostkowy dla A.

Na przykład: Załóżmy, że A jest algorytmem sortowania, a A zapewnia dane do posortowania. Makieta X powinna zapewnić zerową wartość zwracaną, pustą listę danych, pojedynczy element, wiele elementów już posortowanych, wiele elementów jeszcze nie posortowanych, wiele elementów posortowanych wstecz, listy z powtórzeniem tego samego elementu, wartości null zmieszane, śmiesznie duża liczba elementów, a także powinien rzucić wyjątek.

Być może X początkowo zwrócił posortowane dane w poniedziałki i puste listy we wtorki. Ale teraz, gdy X zwraca nieposortowane dane w poniedziałki i zgłasza wyjątki we wtorki, A nie ma znaczenia - te scenariusze zostały już uwzględnione w teście jednostkowym A.

Moby Disk
źródło
co jeśli X właśnie zmienił nazwę indeksu w zwróconej tablicy z foobar na foobarRandomNumber, jak mogę z tym liczyć? jeśli dostaniesz mój punkt, to w zasadzie mój problem, zmieniłem nazwę zwróconej kolumny z secondName na nazwisko, klasyczne zadanie, ale mój test nigdy się nie dowie, ponieważ jest wyśmiewany. Mam po prostu tak dziwne wrażenie, jakby wiele osób w tym pytaniu nigdy tak naprawdę nie próbowało czegoś takiego, zanim skomentuje
FantomX1,
Kompilator powinien był wykryć tę zmianę i zgłosić błąd kompilatora. Jeśli używasz czegoś takiego jak JavaScript, zalecamy przejście na Typescript lub użycie kompilatora takiego jak Babel, który może wykryć takie rzeczy.
Moby Disk
Co się stanie, jeśli używam tablic w PHP, Java lub JavaScript, jeśli zmienisz indeks tablicy lub go usuniesz, kompilatory inne niż te powiedzą ci o tym, indeks może być zagnieżdżony na 36 - przemyślanym numerze zagnieżdżony poziom tablicy, dlatego myślę, że kompilator nie jest rozwiązaniem tego problemu.
FantomX1
-2

Musisz spojrzeć na różne testy.

Same testy jednostkowe będą testować tylko X. Są one po to, aby zapobiec zmianie zachowania X, ale nie zabezpieczyć całego systemu. Zapewniają, że możesz refaktoryzować swoją klasę bez wprowadzania zmian w zachowaniu. A jeśli złamiesz X, złamiesz go ...

Powinien rzeczywiście mock X dla swoich testów jednostkowych i testy z Mock powinien zachować przechodząc nawet po jego zmianie.

Ale jest więcej niż jeden poziom testowania! Istnieją również testy integracyjne. Testy te służą do sprawdzenia interakcji między klasami. Te testy zwykle mają wyższą cenę, ponieważ nie używają próbnych elementów do wszystkiego. Na przykład test integracyjny może faktycznie zapisać rekord w bazie danych, a test jednostkowy nie powinien mieć żadnych zewnętrznych zależności.

Również, jeśli X musi mieć nowe zachowanie, lepiej byłoby podać nową metodę, która zapewniłaby pożądany wynik

Heiko Hatzfeld
źródło