Napisaliśmy prawie 3000 testów - dane zostały zakodowane na stałe, bardzo mało ponownego użycia kodu. Ta metodologia zaczęła nas gryźć w tyłek. Wraz ze zmianami systemu spędzamy więcej czasu na naprawianiu uszkodzonych testów. Posiadamy testy jednostkowe, integracyjne i funkcjonalne.
To, czego szukam, to ostateczny sposób na napisanie łatwych do zarządzania i łatwych do utrzymania testów.
Ramy
.net
unit-testing
Chuck Conway
źródło
źródło
Odpowiedzi:
Nie myśl o nich jako o „zepsutych testach jednostkowych”, ponieważ tak nie jest.
Są to specyfikacje, których Twój program nie obsługuje.
Nie myśl o tym jako o „naprawianiu testów”, ale o „definiowaniu nowych wymagań”.
Testy powinny najpierw określić twoją aplikację, a nie na odwrót.
Nie możesz powiedzieć, że masz działającą implementację, dopóki nie dowiesz się, że działa. Nie możesz powiedzieć, że działa, dopóki go nie przetestujesz.
Kilka innych uwag, które mogą Cię poprowadzić:
źródło
Don't think of it as "fixing the tests", but as "defining new requirements".
To, co opisujesz, może nie być wcale takie złe, ale wskaźnikiem głębszych problemów odkrytych przez twoje testy
Gdybyś mógł zmienić kod, a testy by się nie zepsuły, byłoby to dla mnie podejrzane. Różnica między uzasadnioną zmianą a błędem polega tylko na tym, że jest o to poproszony, a to, o co jest proszone, jest (zakładane TDD) zdefiniowane przez twoje testy.
Dobrze zakodowane dane w testach to dobra rzecz. Testy działają jak fałszerstwa, a nie dowody. Jeśli jest zbyt wiele obliczeń, twoje testy mogą być tautologiami. Na przykład:
Im wyższa abstrakcja, tym bardziej zbliżasz się do algorytmu, a tym samym bliżej porównywania implementacji akutalnej z samym sobą.
Najlepsze ponowne użycie kodu w testach to imho „Checks”, podobnie jak w jUnits
assertThat
, ponieważ zapewniają prostotę testów. Poza tym, jeśli testy można przefaktoryzować w celu współużytkowania kodu, prawdopodobnie może to być również testowany rzeczywisty kod , redukując w ten sposób testy do testów testujących przebudowaną bazę.źródło
Też miałem ten problem. Moje ulepszone podejście wygląda następująco:
Nie pisz testów jednostkowych, chyba że to jedyny dobry sposób na przetestowanie czegoś.
Jestem w pełni przygotowany do przyznania, że testy jednostkowe mają najniższy koszt diagnozy i czasu do naprawy. To czyni je cennym narzędziem. Problem polega na tym, że przy oczywistym przebiegu mogą się różnić, testy jednostkowe są często zbyt małe, aby uzasadnić koszty utrzymania masy kodu. Na dole napisałem przykład, spójrz.
Zastosuj twierdzenia, ilekroć są one równoważne z testem jednostkowym dla tego komponentu. Asercje mają tę fajną właściwość, że zawsze są weryfikowane podczas każdej kompilacji debugowania. Dlatego zamiast testować ograniczenia klasy „Pracownik” w osobnej jednostce testów, skutecznie testujesz klasę Pracownik w każdym przypadku testowym w systemie. Asercje mają również tę fajną właściwość, że nie zwiększają masy kodu tak bardzo, jak testy jednostkowe (które ostatecznie wymagają rusztowania / drwiny / cokolwiek innego).
Zanim ktoś mnie zabije: kompilacje produkcyjne nie powinny ulec awarii na podstawie twierdzeń. Zamiast tego powinni zalogować się na poziomie „Błąd”.
Uwaga dla kogoś, kto jeszcze o tym nie pomyślał, nie należy niczego potwierdzać na temat danych wejściowych użytkownika lub sieci. To ogromny błąd ™.
W moich najnowszych bazach kodu rozsądnie usuwam testy jednostkowe wszędzie tam, gdzie widzę oczywistą okazję do asercji. To znacznie obniżyło ogólne koszty utrzymania i uczyniło mnie znacznie szczęśliwszą osobą.
Preferuj testy systemowe / integracyjne, wdrażając je dla wszystkich swoich podstawowych przepływów i doświadczeń użytkownika. Narożniki prawdopodobnie nie muszą tu być. Test systemowy weryfikuje zachowanie na poziomie użytkownika, uruchamiając wszystkie komponenty. Z tego powodu test systemu jest koniecznie wolniejszy, więc napisz te, które mają znaczenie (nie więcej, nie mniej), a złapiesz najważniejsze problemy. Testy systemu mają bardzo niskie koszty utrzymania.
Należy pamiętać, że ponieważ używasz twierdzeń, każdy test systemu przeprowadzi kilkaset „testów jednostkowych” w tym samym czasie. Masz również pewność, że najważniejsze zostaną uruchomione wiele razy.
Napisz silne interfejsy API, które można przetestować funkcjonalnie. Testy funkcjonalne są niewygodne i (spójrzmy prawdzie w oczy) w pewnym sensie bez znaczenia, jeśli interfejs API utrudnia samodzielną weryfikację działających komponentów. Dobry projekt API a) upraszcza etapy testowania i b) daje jasne i wartościowe stwierdzenia.
Testowanie funkcjonalne jest najtrudniejsze do zrobienia, zwłaszcza gdy masz komponenty komunikujące się jeden do wielu lub (jeszcze gorzej, o Boże) wiele do wielu przez bariery procesowe. Im więcej wejść i wyjść podłączonych do jednego komponentu, tym trudniejsze jest testowanie funkcjonalne, ponieważ musisz odizolować jeden z nich, aby naprawdę przetestować jego funkcjonalność.
W kwestii „nie pisz testów jednostkowych” przedstawię przykład:
Autor tego testu dodał siedem wierszy, które wcale nie przyczyniają się do weryfikacji produktu końcowego. Użytkownik nigdy nie powinien tego widzieć, ponieważ: a) nikt nigdy nie powinien przekazywać NULL (więc napisz więc twierdzenie), lub b) przypadek NULL powinien powodować inne zachowanie. Jeśli przypadek to (b), napisz test, który faktycznie weryfikuje to zachowanie.
Moja filozofia stała się taka, że nie powinniśmy testować artefaktów implementacyjnych. Powinniśmy testować tylko wszystko, co można uznać za rzeczywistą wydajność. W przeciwnym razie nie ma możliwości uniknięcia dwukrotnego zapisania podstawowej masy kodu między testami jednostkowymi (które wymuszają określoną implementację) a samą implementacją.
Należy tutaj zauważyć, że są dobrzy kandydaci do testów jednostkowych. W rzeczywistości istnieje nawet kilka sytuacji, w których test jednostkowy jest jedynym odpowiednim środkiem do weryfikacji czegoś, w którym bardzo ważne jest napisanie i utrzymanie tych testów. Na szczycie mojej listy ta lista zawiera nietrywialne algorytmy, odsłonięte pojemniki danych w interfejsie API oraz wysoce zoptymalizowany kod, który wydaje się „skomplikowany” (inaczej „następny facet prawdopodobnie to spieprzy”.).
Moja konkretna rada dla ciebie: zacznij ostrożnie usuwać testy jednostkowe, gdy się psują, zadając sobie pytanie: „czy to wynik, czy marnuję kod?” Prawdopodobnie uda ci się zmniejszyć liczbę rzeczy, które marnują Twój czas.
źródło
Wydaje mi się, że twoje testy jednostkowe działają jak urok. Jest to dobra rzecz, że to jest tak kruche do zmian, ponieważ jest to rodzaj całego punktu. Małe zmiany w testach łamania kodu, aby wyeliminować możliwość wystąpienia błędu w całym programie.
Należy jednak pamiętać, że naprawdę trzeba tylko testować warunki, które spowodowałyby niepowodzenie metody lub nieoczekiwane wyniki. To sprawiłoby, że twoje testy jednostkowe byłyby bardziej podatne na „zerwanie”, jeśli istnieje prawdziwy problem, niż trywialne rzeczy.
Chociaż wydaje mi się, że mocno przeprojektowujesz program. W takich przypadkach zrób wszystko, czego potrzebujesz, i usuń stare testy, a następnie zastąp je nowymi. Naprawianie testów jednostkowych jest opłacalne tylko wtedy, gdy nie naprawiasz ich z powodu radykalnych zmian w twoim programie. W przeciwnym razie może się okazać, że poświęcasz zbyt dużo czasu na przepisywanie testów, aby można je było zastosować w nowo napisanej sekcji kodu programu.
źródło
Jestem pewien, że inni będą mieli znacznie większy wkład, ale z mojego doświadczenia wynika, że są to niektóre ważne rzeczy, które pomogą ci:
źródło
Obsługuj testy tak jak robisz to z kodem źródłowym.
Kontrola wersji, wydania punktów kontrolnych, śledzenie problemów, „własność funkcji”, planowanie i szacowanie wysiłków itp. Tam już to zrobiono - myślę, że jest to najbardziej skuteczny sposób radzenia sobie z opisanymi przez ciebie problemami.
źródło
Zdecydowanie powinieneś rzucić okiem na wzorce testowe XUnit Gerarda Meszarosa . Ma świetną sekcję z wieloma przepisami na ponowne użycie kodu testowego i uniknięcie powielania.
Jeśli twoje testy są kruche, może to oznaczać, że nie uciekasz się wystarczająco do testowania podwójnych. W szczególności, jeśli odtwarzasz całe wykresy obiektów na początku każdego testu jednostkowego, sekcje Rozmieść w testach mogą być zbyt duże i często możesz znaleźć się w sytuacjach, w których musisz przepisać sekcje Rozmieść w znacznej liczbie testów tylko dlatego, że jedna z najczęściej używanych klas uległa zmianie. Próbki i odcinki mogą ci w tym pomóc, ograniczając liczbę obiektów, które musisz nawodnić, aby uzyskać odpowiedni kontekst testowy.
Usunięcie nieistotnych szczegółów z ustawień testowych za pomocą próbnych i kodów pośredniczących i zastosowanie wzorców testowych do ponownego użycia kodu powinno znacznie zmniejszyć ich kruchość.
źródło