2009-05-04 12 views
10

Już od jakiegoś czasu przygotowałem "McDell'a" Code Complete ". Teraz przeczytałem go ponownie w Hunt & Thomasa "The Pragmatic Programmer": Użyj twierdzeń! Uwaga: Twierdzenia o braku testów jednostkowych, czyli Debug.Assert().Czy testowanie jednostek powoduje, że Debug.Assert() jest niepotrzebny?

Po pytaniach SO When should I use Debug.Assert()? i When to use assertion over exceptions in domain classes twierdzenia są użyteczne dla rozwoju, ponieważ sytuacje "niemożliwe" można znaleźć dość szybko. I wydaje się, że są one powszechnie używane. O ile rozumiałem twierdzenia, w języku C# są często używane do sprawdzania zmiennych wejściowych dla "niemożliwych" wartości.

Aby testy jednostkowe były zwięzłe i izolowane w jak największym stopniu, robię klasy i metody dla feedu z danymi wejściowymi "niemoŜliwym" (jak pusty ciąg znaków).

Takie testy wyraźnie dokumentują, że nie polegają na określonych danych wejściowych. Uwaga: ćwiczę, co "wzorce testowe" Meszarosa "opisuję jako Minimal Fixture.

I o to właśnie chodzi: gdybym miał zapewnione osłony chroniące te dane, to wysadziłby moje testy jednostkowe.

Podoba mi się pomysł programowania asertywnego, ale z drugiej strony nie muszę go wymuszać. Obecnie nie mogę myśleć o jakimkolwiek użyciu dla Debug.Assert(). Może jest coś, za czym tęsknię? Czy masz jakieś sugestie, gdzie mogą być przydatne? Może po prostu przeceniam użyteczność twierdzeń? A może mój sposób testowania musi zostać ponownie przeanalizowany?

Edytuj: Best practice for debug Asserts during Unit testing jest bardzo podobny, ale nie odpowiada na pytanie, które mi przeszkadza: czy powinienem zadbać o Debug.Assert() w języku C#, czy testuję tak, jak to opisałem? Jeśli tak, to w jakiej sytuacji są one przydatne? Z mojego obecnego punktu widzenia takie testy jednostek sprawiają, że Debug.Assert() jest niepotrzebne.

Kolejny punkt: jeśli naprawdę tak uważasz, jest to duplikat pytania, po prostu dodaj komentarz.

+1

Należy zauważyć, że struktura testowa programu Visual Studio nie * traktuje błędu Debug.Assert jako niepowodzenia testu jednostkowego. – yoyo

Odpowiedz

5

Teoretycznie masz rację - wyczerpujące testy sprawiają, że zwolnienia są zbyteczne. W teorii. W parctice są nadal przydatne do debugowania twoich testów i do łapania prób przez przyszłych programistów, którzy mogą próbować używać interfejsów niezgodnie z ich zamierzoną semantyką.

Podsumowując, służą one wyłącznie do celów innych niż testy jednostkowe. Są po to, aby łapać błędy, które z natury rzeczy nie zostaną popełnione podczas pisania testów jednostkowych.

Zalecam ich przechowywanie, ponieważ oferują one kolejny poziom ochrony przed błędami programistów.

Są również lokalnymi mechanizmami ochrony przed błędami, podczas gdy testy jednostkowe są niezależne od testowanego kodu. Znacznie łatwiej jest "nieumyślnie" wyłączyć testy jednostkowe pod presją, niż wyłączyć wszystkie potwierdzenia i sprawdzenia w czasie wykonywania w kawałku kodu.

+0

"... za złapanie prób przez przyszłych programistów, którzy mogą próbować używać interfejsów niezgodnie z ich zamierzoną semantyką.". Hmmm ... Mogę myśleć o niszach, gdzie są one użyteczne: kiedy semantyka interfejsu nie jest widoczna, umieszczenie twierdzenia czyni to bardzo wyraźnym. –

+0

Zastanawiając się nad tym, ma to całkiem sens: używanie asercji w ten sposób jest korzystne podczas programowania i ma na celu odkrycie błędów programistycznych (raczej pewien błąd wejściowy od użytkownika/bazy danych/usługi internetowej/...) –

+0

Dokładnie. Jest to kolejny aspekt programowania na podstawie kontraktu, w którym kontrakt nie może być egzekwowany w czasie kompilacji, ale środowisko wykonawcze może wychwytywać naruszenia. –

2

Jeśli przeprowadzasz wyczerpujące testy jednostkowe, które obejmują wszystkie przypadki, w których możesz napotkać dziwne, skrajne przypadki, to nie sądzę, abyś uznał twierdzenia za bardzo użyteczne. Większość osób, które nie testują twierdzeń o miejscu testowania, by ustalić podobne ograniczenia, które złapią podczas testów.

+0

Dla mnie oznacza to: Asercje są dobre, gdy nie ma testów jednostkowych. Chociaż nie mogą zastąpić testów jednostkowych, jest to lepsze niż nic. –

+1

Szczerze mówiąc, powiedziałbym, że twierdzenia (w formie, w jakiej używa ich większość ludzi) to prawie to samo, co testy jednostkowe. Ludzie zwykle trzymają je, aby sprawdzić, czy dane wejściowe wchodzące w funkcję są czymś, z czym mogą sobie poradzić, i że wyjście z funkcji jest tym, czego oczekują, co jest dokładnie tym, co powinien wykonać test jednostki, jeśli testuje tę funkcję. . Największą różnicą jest to, że twój pakiet testów jednostkowych często nie jest w tym samym miejscu, co twój kod, podczas gdy asercje są wbudowane. – mquander

+0

Oczywiście, asercje czasami sprawdzają również bardziej drobiazgi niż testy jednostkowe, np. Upewniając się, że pamięć została poprawnie przydzielona lub że połączenie zostało wykonane prawidłowo. – mquander

3

Używam zarówno testów jednostkowych, jak i zapewnień, do różnych celów.

Testy jednostkowe to zautomatyzowane eksperymenty pokazujące, że twój program (i jego części) działa zgodnie z określonymi. Podobnie jak w matematyce eksperymentowanie nie jest dowodem, o ile nie można wypróbować każdej możliwej kombinacji danych wejściowych. Nic nie demonstruje tego lepiej niż fakt, że nawet przy testach jednostkowych Twój kod będzie zawierał błędy. Niewiele, ale będzie je mieć.

Asercje służą do przechwytywania podejrzanych sytuacji w czasie wykonywania, które normalnie nie powinny się zdarzać. Być może słyszałeś o warunkach wstępnych, warunkach końcowych, niezmiennych pętlach i podobnych rzeczach. W realnym świecie nie często przechodzimy przez formalny proces faktycznego udowodnienia (logiką formalną), że fragment kodu dostarcza określonych warunków końcowych, jeśli warunki wstępne są spełnione. To byłby prawdziwy matematyczny dowód, ale często nie mamy czasu, aby to zrobić dla każdej metody. Jednak sprawdzając, czy warunki wstępne i warunki końcowe są spełnione, możemy zauważyć problemy na znacznie wcześniejszym etapie.

+0

Nie robię tego w 100% z testowania jednostkowego, ale myślałem, że to, co dotyczy warunków wstępnych i warunków końcowych, jest w istocie; zautomatyzowane testowanie, które naprawdę obejmowało wszystkie twoje krańcowe przypadki, nakarmiło wszystkie możliwe rodzaje danych, które mogą wejść i przetestowało wszystkie wychodzące dane. – mquander

+1

Asercje nie są jawnie tworzone dla sprawdzania warunków wstępnych i warunków końcowych, ale nadal są przydatne do emulacji. To kolejny punkt, w którym twierdzenia są użyteczne. –

1

Myślę, że ideą Unit Testing w tym przypadku jest przeniesienie tych twierdzeń na przypadki testowe, aby upewnić się, że zamiast Debug.Assert (...) testowany kod obsługuje go bez rzucania (lub zapewnia poprawne wyrzucenie).

4

Generalnie widzę, że przyzwyczajenia są używane do sprawdzania poprawności stanu wewnętrznego, a nie do sprawdzania argumentów.

IMO wejścia do solidnego interfejsu API powinny być chronione przez kontrole, które pozostają na miejscu, niezależnie od typu kompilacji. Na przykład, jeśli publiczna metoda oczekuje argumentu będącego liczbą w zakresie od 5 do 500, powinien być strzeżony przez ArgumentOutOfRangeException. Nie działa szybko i nie udaje się często używać wyjątków, o ile mi wiadomo, szczególnie gdy argument jest gdzieś popychany i jest używany znacznie później.

Jednak w miejscach, w których wewnętrzny stan przejściowy jest sprawdzany pod kątem stanu zdatności (na przykład sprawdzanie, czy jakiś stan pośredni jest w rozsądnych granicach podczas pętli), wygląda na to, że Debug.Assert jest bardziej w domu. Co jeszcze masz zamiar zrobić, gdy twój algorytm się nie pomyli pomimo przekazania do niego prawidłowych argumentów? Zgłasza wyjątek EpicFailException? :) Myślę, że to właśnie tam Debug.Assert jest nadal przydatny.

Nadal jestem niezdecydowany, jeśli chodzi o najlepszą równowagę między tymi dwoma. Przestałem używać używania Debug.Aserts tak wiele w C# od czasu rozpoczęcia testów jednostkowych, ale wciąż jest dla nich miejsce dla IMO. Na pewno nie użyłbym ich do sprawdzenia poprawności w używaniu API, ale po to, żeby sprawdzić, czy nie ma problemów z dostaniem się do miejsc. Pewnie.

Jedynym minusem jest to, że mogą wyskoczyć i zatrzymać NUnit, ale można napisać wtyczkę NUnit, aby je wykryć i nie wykonać żadnego testu, który uruchamia potwierdzenie.

+1

Lol na EpicFailException ... Będę musiał użyć tego w moim kodzie (w połączeniu z Over9000Exception). –

+0

"np. Sprawdzanie, czy jakiś stan pośredni jest w granicach rozsądku podczas pętli" ... Asercje do sprawdzania niezmienników pętli! Trafne spostrzeżenie. To jest coś, czego naprawdę nie mogę sprawdzić za pomocą testów jednostkowych (przynajmniej nie z mniej lub bardziej kpiącymi i wprowadzającymi zmiennymi szpiegowskimi). –

+0

Te wyskakujące okienka są dość denerwujące ... nie wiem, co się dzieje, kiedy twierdzenie pojawia się, gdy zdarza się w serwisie Windows ... może być warte innego pytania ;-) –