Jestem głęboko przekonany o wartości stosowania testów weryfikujących pełny program (np. Testy konwergencji), w tym zautomatyzowanego zestawu testów regresji . Po przeczytaniu kilku książek o programowaniu, miałem dokuczliwe wrażenie, że „powinienem” napisać testy jednostkowe (tj. Testy, które weryfikują poprawność pojedynczej funkcji i nie sprowadzają się do uruchomienia całego kodu w celu rozwiązania problemu). . Jednak testy jednostkowe nie zawsze wydają się pasować do kodów naukowych i kończą się sztucznym uczuciem lub stratą czasu.
Czy powinniśmy pisać testy jednostkowe dla kodów badań?
programming-paradigms
testing
David Ketcheson
źródło
źródło
Odpowiedzi:
Przez wiele lat nie rozumiałem, że nie mam wystarczająco dużo czasu na napisanie testów jednostkowych dla mojego kodu. Kiedy pisałem testy, były rozdęte, ciężkie rzeczy, które tylko zachęciły mnie do myślenia, że powinienem pisać testy jednostkowe tylko wtedy, gdy wiedziałem, że są potrzebne.
Potem zacząłem używać Test Driven Development i odkryłem, że jest to pełne odkrycie. Jestem teraz głęboko przekonany, że nie mam czasu, aby nie pisać testów jednostkowych .
Z mojego doświadczenia wynika, że rozwijając się z myślą o testowaniu, otrzymujesz czystsze interfejsy, bardziej skoncentrowane klasy i moduły oraz ogólnie bardziej SOLIDNY , testowalny kod.
Za każdym razem, gdy pracuję ze starszym kodem, który nie ma testów jednostkowych i muszę coś ręcznie przetestować, ciągle myślę „byłoby o wiele szybciej, gdyby ten kod miał już testy jednostkowe”. Za każdym razem, gdy próbuję dodać funkcjonalność testu jednostkowego do kodu z wysokim sprzężeniem, ciągle myślę „byłoby to o wiele łatwiejsze, gdyby zostało napisane w sposób oddzielony”.
Porównywanie i kontrastowanie dwóch stacji eksperymentalnych, które popieram. Jeden istnieje już od jakiegoś czasu i ma dużo starszego kodu, a drugi jest stosunkowo nowy.
Dodając funkcjonalność do starego laboratorium, często chodzi o to, aby dostać się do laboratorium i spędzić wiele godzin pracując nad implikacjami potrzebnej funkcjonalności i jak mogę dodać tę funkcjonalność bez wpływu na żadną inną funkcjonalność. Kod po prostu nie jest skonfigurowany do testowania w trybie off-line, więc prawie wszystko musi być opracowane online. Gdybym spróbował opracować off-line, skończyłbym z większą ilością fałszywych obiektów, niż byłoby to uzasadnione.
W nowszym laboratorium zwykle mogę dodać funkcjonalność, rozwijając ją offline przy biurku, kpiąc sobie tylko z tych rzeczy, które są natychmiast potrzebne, a następnie spędzając tylko krótki czas w laboratorium, rozwiązując wszelkie pozostałe problemy, które nie zostały usunięte -linia.
Dla jasności, a ponieważ @ naught101 poprosił ...
Zwykle pracuję nad oprogramowaniem do kontroli eksperymentalnej i akwizycji danych, z pewną analizą danych ad hoc, więc połączenie TDD z kontrolą wersji pomaga dokumentować zarówno zmiany w podstawowym sprzęcie eksperymentalnym, jak i zmiany wymagań w zakresie gromadzenia danych w czasie.
Jednak nawet w sytuacji rozwijania kodu eksploracyjnego widziałem znaczącą korzyść z kodyfikacji założeń, a także możliwość zobaczenia, jak te założenia ewoluują w czasie.
źródło
Kody naukowe zwykle zawierają konstelacje blokujących się funkcji częściej niż kody biznesowe, nad którymi pracowałem, zwykle z powodu matematycznej struktury problemu. Nie sądzę więc, aby testy jednostkowe dla poszczególnych funkcji były bardzo skuteczne. Wydaje mi się jednak, że istnieje klasa testów jednostkowych, które są skuteczne i wciąż różnią się od testów całego programu tym, że są ukierunkowane na określoną funkcjonalność.
Po prostu krótko określam, co mam na myśli przez tego rodzaju testy. Testy regresji szukają zmian w istniejącym zachowaniu (jakoś sprawdzonym), gdy wprowadzane są zmiany w kodzie. Testy jednostkowe uruchamiają fragment kodu i sprawdzają, czy daje on pożądany wynik na podstawie specyfikacji. Nie różnią się tak bardzo, ponieważ oryginalny test regresji był testem jednostkowym, ponieważ musiałem ustalić, czy dane wyjściowe są prawidłowe.
Moim ulubionym przykładem numerycznego testu jednostkowego jest testowanie stopnia zbieżności implementacji elementu skończonego. Z pewnością nie jest to proste, ale wymaga znanego rozwiązania PDE, powoduje szereg problemów przy zmniejszaniu rozmiaru oczek , a następnie dopasowuje normę błędu do krzywej C h r, gdzie r jest współczynnikiem zbieżności. Robię to dla problemu Poissona w PETSc przy użyciu Pythona. Nie szukam różnicy, jak w regresji, ale szczególnie szybkość r określoną dla danego elementu.h dohr r r
Dwa kolejne przykłady testów jednostkowych, pochodzące z PyLith , to lokalizacja punktowa, która jest pojedynczą funkcją, która jest łatwa do uzyskania wyników syntetycznych, oraz tworzenie spójnych komórek o zerowej objętości w siatce, która obejmuje kilka funkcji, ale dotyczy ograniczonego fragmentu funkcjonalność w kodzie.
Istnieje wiele tego rodzaju testów, w tym testy zachowania i spójności. Operacja nie różni się tak bardzo od regresji (uruchamiasz test i porównujesz dane wyjściowe ze standardem), ale standardowe dane wyjściowe pochodzą ze specyfikacji, a nie z poprzedniego uruchomienia.
źródło
Odkąd przeczytałem o rozwoju opartym na testach w Code Complete, wydanie drugie , korzystałem z platformy do testowania jednostkowegow ramach mojej strategii rozwoju, a to znacznie zwiększyło moją wydajność poprzez zmniejszenie ilości czasu poświęcanego na debugowanie, ponieważ różne testy, które piszę, są diagnostyczne. Jako korzyść uboczną jestem o wiele bardziej przekonany o swoich wynikach naukowych i wielokrotnie korzystałem z testów jednostkowych w celu obrony moich wyników. Jeśli w teście jednostkowym wystąpił błąd, zazwyczaj dość szybko mogę dowiedzieć się, dlaczego. Jeśli moja aplikacja ulegnie awarii i wszystkie testy jednostkowe zakończą się pomyślnie, przeprowadzam analizę pokrycia kodu, aby zobaczyć, które części mojego kodu nie są wykonywane, a także przeglądam kod za pomocą debugera, aby wskazać źródło błędu. Następnie piszę nowy test, aby upewnić się, że błąd pozostanie naprawiony.
Wiele testów, które piszę, nie są czystymi testami jednostkowymi. Ściśle zdefiniowane testy jednostkowe powinny wykonywać funkcje jednej funkcji. Kiedy mogę łatwo przetestować pojedynczą funkcję przy użyciu fałszywych danych, robię to. Innym razem nie mogę łatwo kpić z danych potrzebnych do napisania testu sprawdzającego funkcjonalność danej funkcji, więc przetestuję tę funkcję wraz z innymi w teście integracji. Testy integracyjneprzetestuj zachowanie wielu funkcji jednocześnie. Jak zauważa Matt, kody naukowe są często konstelacją blokujących się funkcji, ale często niektóre funkcje są wywoływane kolejno, a testy jednostkowe mogą być zapisywane w celu przetestowania danych wyjściowych na etapach pośrednich. Na przykład, jeśli mój kod produkcyjny wywołuje kolejno pięć funkcji, napiszę pięć testów. Pierwszy test wywoła tylko pierwszą funkcję (więc jest to test jednostkowy). Następnie drugi test wywoła pierwszą i drugą funkcję, trzeci test wywoła pierwsze trzy funkcje i tak dalej. Nawet gdybym mógł napisać testy jednostkowe dla każdej funkcji w kodzie, i tak napisałbym testy integracji, ponieważ błędy mogą powstać, gdy łączone są różne moduły programu. Wreszcie, po napisaniu wszystkich testów jednostkowych i testów integracyjnych, których potrzebuję, „ Zakończę swoje studia przypadków testami jednostkowymi i wykorzystam je do testów regresji, ponieważ chcę, aby moje wyniki były powtarzalne. Jeśli nie są powtarzalne, a otrzymuję różne wyniki, chcę wiedzieć, dlaczego. Niepowodzenie testu regresji może nie być prawdziwym problemem, ale zmusi mnie do ustalenia, czy nowe wyniki są co najmniej tak samo wiarygodne jak stare wyniki.
Warto także wraz z testowaniem jednostkowym przeprowadzać analizę statyczną kodu, debugowanie pamięci i kompilację z flagami ostrzegawczymi kompilatora, aby wychwycić proste błędy i nieużywany kod.
źródło
Z mojego doświadczenia wynika, że wraz ze wzrostem złożoności kodów badań naukowych potrzebne jest bardzo modułowe podejście do programowania. Może to być bolesne w przypadku kodów z dużymi i starożytnymi (
f77
ktoś?), Ale konieczne jest przejście do przodu. Ponieważ moduł buduje się wokół określonego aspektu kodu (w przypadku aplikacji CFD, pomyśl Warunki brzegowe lub termodynamika), testowanie jednostkowe jest bardzo cenne w celu sprawdzenia poprawności nowej implementacji oraz wyodrębnienia problemów i dalszego rozwoju oprogramowania.Te testy jednostkowe powinny znajdować się jeden poziom poniżej weryfikacji kodu (czy mogę odzyskać analityczne rozwiązanie mojego równania falowego?) I 2 poziomy poniżej weryfikacji kodu (czy mogę przewidzieć prawidłowe wartości szczytowe RMS w moim turbulentnym przepływie rur), po prostu zapewniając, że programowanie (czy argumenty są poprawnie przekazywane, czy wskaźniki wskazują właściwą rzecz?) i „matematyka” (ta podprogram oblicza współczynnik tarcia. Jeśli wprowadzę zestaw liczb i obliczę rozwiązanie ręcznie, czy procedura daje to samo wynik?) są poprawne. Zasadniczo idzie o jeden poziom powyżej tego, co kompilatory mogą wykryć, tj. Podstawowe błędy składniowe.
Zdecydowanie poleciłbym go przynajmniej dla niektórych kluczowych modułów w twojej aplikacji. Trzeba jednak zdać sobie sprawę z tego, że jest to niezwykle żmudne i czasochłonne, więc jeśli nie masz nieograniczonej siły roboczej, nie poleciłbym tego w 100% złożonego kodu.
źródło
Testy jednostkowe kodów naukowych są przydatne z różnych powodów.
Trzy w szczególności to:
Testy jednostkowe pomagają innym osobom zrozumieć ograniczenia twojego kodu. Zasadniczo testy jednostkowe są formą dokumentacji.
Testy jednostkowe sprawdzają, czy pojedyncza jednostka kodu zwraca poprawne wyniki, i upewniają się, że zachowanie programu nie zmienia się po zmianie szczegółów.
Korzystanie z testów jednostkowych ułatwia modularyzację kodów badań. Może to być szczególnie ważne, jeśli zaczniesz próbować ukierunkować swój kod na nowej platformie, na przykład chcesz go zrównoleglić lub uruchomić na komputerze GPGPU.
Przede wszystkim testy jednostkowe dają pewność, że wyniki badań, które tworzysz przy użyciu swoich kodów, są prawidłowe i możliwe do zweryfikowania.
Zwracam uwagę, że w pytaniu wspominasz o testach regresji. W wielu przypadkach testy regresji osiąga się poprzez automatyczne, regularne wykonywanie testów jednostkowych i / lub testów integracyjnych (które sprawdzają, czy fragmenty kodu działają poprawnie po połączeniu; w obliczeniach naukowych często wykonuje się to przez porównanie danych wyjściowych z danymi eksperymentalnymi lub wyniki wcześniejszych zaufanych programów). Wygląda na to, że z powodzeniem stosujesz już testy integracyjne lub test jednostkowy na poziomie dużych złożonych komponentów.
Powiedziałbym, że ponieważ kody badawcze stają się coraz bardziej złożone i opierają się na kodach i bibliotekach innych osób, ważne jest, aby zrozumieć, gdzie pojawia się błąd. Testowanie jednostkowe pozwala znacznie łatwiej zlokalizować błąd.
Opis, dowody i odniesienia można znaleźć w części 7 „Planowanie błędów” artykułu, którego współautorem na temat najlepszych praktyk w dziedzinie obliczeń naukowych, użyteczne - wprowadza także uzupełniającą koncepcję programowania obronnego.
źródło
W moich deal.II klas I uczyć, że oprogramowanie, które nie ma testów nie działa prawidłowo (i dalej na stres, że celowo powiedział „ ma nie działać poprawnie”, a nie " może nie działać poprawnie).
Oczywiście żyję zgodnie z mantrą - tak właśnie się dzieje. Przyszło mi przeprowadzić 2500 testów przy każdym zatwierdzeniu ;-)
Mówiąc poważniej, myślę, że Matt dobrze zdefiniował już dwie klasy testów. Piszemy testy jednostkowe dla rzeczy niższego poziomu i to naturalnie przechodzi do testów regresji dla rzeczy wyższego poziomu. Nie sądzę, że mógłbym wyznaczyć wyraźną granicę, która rozdzieliłaby nasze testy na jedną lub drugą stronę. Z pewnością wielu z nich kroczy po linii, w której ktoś spojrzał na wynik i stwierdził, że jest on w dużej mierze uzasadniony (test jednostkowy?) bez przyjrzenia się temu z ostatnią dokładnością (test regresji?).
źródło
Tak i nie. Z pewnością nie jest to zgodne z podstawowymi procedurami podstawowego zestawu narzędzi, których używasz, aby ułatwić sobie życie, takimi jak procedury konwersji, odwzorowania ciągów, podstawowa fizyka i matematyka itp. Jeśli chodzi o klasy obliczeniowe lub funkcje, mogą one generalnie wymagać długiego czasu pracy, oraz możesz faktycznie chcieć przetestować je jako testy funkcjonalne, a nie jako jednostki. Ponadto, jedniutkie i bardzo stresujące są te klasy i podmioty, których poziom i zastosowanie bardzo się zmienią (np. W celach optymalizacji) lub których dane wewnętrzne zostaną zmienione z jakiegokolwiek powodu. Najbardziej typowym przykładem jest klasa owijająca ogromną macierz, zmapowaną z dysku.
źródło
Absolutnie!
Co ci nie wystarcza?
W programowaniu naukowym bardziej niż jakikolwiek inny rodzaj, rozwijamy się w oparciu o próbę dopasowania systemu fizycznego. Skąd będziesz wiedzieć, czy zrobiłeś to inaczej niż poprzez testowanie? Zanim zaczniesz kodować, zdecyduj, w jaki sposób zamierzasz używać kodu i opracuj kilka przykładowych uruchomień. Spróbuj złapać wszelkie możliwe przypadki krawędzi. Zrób to w sposób modułowy - na przykład dla sieci neuronowej możesz wykonać zestaw testów dla pojedynczego neuronu i zestaw testów dla kompletnej sieci neuronowej. W ten sposób, kiedy zaczniesz pisać kod, możesz upewnić się, że twój neuron działa, zanim zaczniesz pracować w sieci. Praca w takich etapach oznacza, że gdy napotkasz problem, masz tylko najnowszy „etap” kodu do przetestowania, wcześniejsze etapy zostały już przetestowane.
Dodatkowo, po ukończeniu testów, jeśli musisz przepisać kod w innym języku (na przykład konwertowanie do CUDA), a nawet jeśli tylko go aktualizujesz, masz już skrzynki testowe i możesz ich użyć do wykonania upewnij się, że obie wersje programu działają w ten sam sposób.
źródło
Tak.
Pomysł, że każdy kod jest napisany bez testów jednostkowych, jest anatemą. Chyba że udowodnisz poprawność kodu, a następnie udowodnisz poprawność = P.
źródło
Do tego pytania podchodzę raczej pragmatycznie niż dogmatycznie. Zadaj sobie pytanie: „Co może pójść nie tak w funkcji X?” Wyobraź sobie, co dzieje się z danymi wyjściowymi, gdy wprowadzasz do kodu kilka typowych błędów: zły prefaktor, zły indeks, ... A potem pisz testy jednostkowe, które mogą wykryć tego rodzaju błąd. Jeśli dla danej funkcji nie ma sposobu na napisanie takich testów bez powtórzenia kodu samej funkcji, to nie - ale pomyśl o testach na wyższym poziomie.
O wiele ważniejszym problemem związanym z testami jednostkowymi (a właściwie wszelkimi testami) w kodzie naukowym jest sposób radzenia sobie z niepewnościami arytmetyki zmiennoprzecinkowej. O ile mi wiadomo, nie ma jeszcze dobrych ogólnych rozwiązań.
źródło
Żal mi Tangureny - tutaj mantra brzmi „Nieprzetestowany kod to uszkodzony kod” i pochodzi od szefa. Zamiast powtarzać wszystkie dobre powody przeprowadzania testów jednostkowych, chcę tylko dodać kilka szczegółów.
źródło
Testy jednostkowe zastosowałem z dobrym skutkiem na kilku niewielkich kodach (tj. Jednym programatorze), w tym trzecim wersji mojego kodu analizy rozprawy w fizyce cząstek.
Pierwsze dwie wersje zawaliły się pod własnym ciężarem i zwielokrotnieniem wzajemnych połączeń.
Inni napisali, że interakcja między modułami jest często miejscem, w którym psuje się kodowanie naukowe, i mają rację. Ale to jest o wiele łatwiej zdiagnozować te problemy, gdy można wykazać, że każdy moduł niezbicie jest robić to, co ona ma zrobić.
źródło
Nieco innym podejściem, które zastosowałem przy opracowywaniu solwera chemicznego (dla złożonych domen geologicznych), było to, co można nazwać testowaniem jednostkowym metodą kopiowania i wklejania fragmentu .
Zbudowanie wiązki testowej dla oryginalnego kodu osadzonego w dużym modelarzu układu chemicznego nie było możliwe w czasie.
Udało mi się jednak opracować coraz bardziej złożony zestaw urywków pokazujących działanie parsera (Wzmocnienie ducha) dla formuł chemicznych, jako testów jednostkowych dla różnych wyrażeń.
Ostatni, najbardziej złożony test jednostkowy był bardzo zbliżony do kodu potrzebnego w systemie, bez konieczności zmiany tego kodu, aby można go było wyśmiewać. Dzięki temu mogłem skopiować cały kod przetestowany przez jednostkę.
To, co sprawia, że jest to coś więcej niż tylko ćwiczenie uczenia się i prawdziwy zestaw regresji, to dwa czynniki - testy jednostkowe przechowywane w głównym źródle i uruchamiane jako część innych testów dla tej aplikacji (i tak, wykryły efekt uboczny z Boost Duch zmienia się 2 lata później) - ponieważ kod skopiowany i wklejony został minimalnie zmodyfikowany w prawdziwej aplikacji, mógł mieć komentarze odnoszące się do testów jednostkowych, aby pomóc komuś utrzymać je w synchronizacji.
źródło
W przypadku większych baz kodu przydatne są testy (niekoniecznie testy jednostkowe) elementów wysokiego poziomu. Testy jednostkowe dla niektórych prostszych algorytmów są również przydatne, aby upewnić się, że Twój kod nie robi bzdur, ponieważ funkcja pomocnicza używa
sin
zamiastcos
.Jednak dla całego kodu badań bardzo trudno jest pisać i utrzymywać testy. Algorytmy są zwykle duże bez znaczących wyników pośrednich, które mogą mieć oczywiste testy i często ich uruchomienie zajmuje dużo czasu, zanim pojawi się wynik. Oczywiście można testować w odniesieniu do przebiegów referencyjnych, które miały dobre wyniki, ale nie jest to dobry test w sensie testu jednostkowego.
Wyniki często są przybliżeniami prawdziwego rozwiązania. Chociaż możesz przetestować swoje proste funkcje, jeśli są one dokładne do pewnego epsilon, bardzo trudno będzie zweryfikować, czy np. Pewna siatka wyników jest poprawna, czy nie, co zostało wcześniej ocenione przez użytkownika (ciebie).
W takich przypadkach automatyczne testy często mają zbyt wysoki stosunek kosztów do korzyści. Polecam coś lepszego: pisz programy testowe. Na przykład napisałem średniej wielkości skrypt Pythona do tworzenia danych o wynikach, takich jak histogramy rozmiarów krawędzi i kątów siatki, obszar największego i najmniejszego trójkąta i ich stosunek itp.
Mogę go zarówno użyć do oceny siatek wejściowych, jak i wyjściowych podczas normalnej pracy i użyć go do sprawdzenia poprawności po zmianie algorytmu. Kiedy zmieniam algorytm, nie zawsze wiem, czy nowy wynik jest lepszy, ponieważ często nie ma absolutnej miary, która aproksymacja jest najlepsza. Ale generując takie wskaźniki, mogę powiedzieć o niektórych czynnikach, co jest lepsze: „Nowy wariant ma ostatecznie lepszy stosunek kątów, ale gorszy współczynnik konwergencji”.
źródło