Strona główna » Kursy » Kurs C# » Poszukiwanie Błędów
 

Poszukiwanie Błędów

Wstęp

Poszukiwania błędów w kodzie programu mogą trwać bardzo długo. Istnieje szereg narzędzi, które pozwalają ten proces przyśpieszyć. Jednym z nich są potwierdzenia, które pozwalają przygotować testy jednostkowe do wybranego fragmentu programu. W tym artykule zostanie przedstawiona klasa Assert wyłapująca błędy w programie.

Klasa

Klasa Assert jest dostępna po zaimportowaniu System.Diagnostics. Zawiera ona szereg różnych metod, które mogą okazać się bardzo pomocne podczas testowania kodu. Część z nich wyświetla błąd wtedy i tylko wtedy, gdy przekazany warunek na poprawność działania nie zostanie spełniony. W większości przypadków pomimo pokazania błędu program może kontynuować pracę, więc możliwe jest prześledzenie jak niepoprawny wynik wpływa na dalsze działanie programu.

Implementacja

Przyśpieszenia Algorytmu

Algorytmy mogą się różnić pod względem efektywności, ale jeśli mają spełniać to samo zadanie w programie to ich wyniki muszą być identyczne. Zazwyczaj dla algorytmów można przygotować zestaw danych wejściowych i oczekiwanych wyników, a następnie dla każdego zestawu sprawdzić co zwróci algorytm. Niezgodność chociaż jednego z nich może świadczyć o to, że algorytm popełnia błąd. Czase, nie istnieje potrzeba przygotowywania oczekiwanych danych wejściowych, ponieważ można je wyliczyć mniej efektywnym, ale sprawdzonym algorytmem.

Przykładem może być tutaj Szybkie Potęgowanie. Nic nie szkodzi na przeszkodzie, aby taki algorytm przetestować przy pomocy zwykłej funkcji potęgującej. Należy jednak pamiętać, aby porównywać wyniki zwrócone przez funkcje dla tych samych danych wejściowych. Oto kod do przetestowania:

  1. static double potega(double x, int n) {
  2.   if (n == 0)
  3.     return 1;
  4.   if (n % 2 != 0) {
  5.     return x * potega(x, n - 1);
  6.   } else {
  7.     return Math.Pow(potega(x, n / 2), 2.0);
  8.   }
  9. }

Poniżej została zamieszczona funkcja, która służy do testowania przedstawionego wyżej algorytmu. W pętli dla tych samych danych oblicza wynik zwrócony przez normalne potęgowanie oraz szybkiePotegowanie().

  1. static void Test() {
  2.   for(int i = 2; i <= 10; i++) {
  3.     double wynik = potega(i, i - 1);
  4.     double wzorzec = Math.Pow(i, i - 1);
  5.     Debug.Assert(wynik == wzorzec);
  6.   }
  7. }

Uzyskane wyniki są porównywane w linijce (5.). W przypadku, gdyby wyniki nie zgadzały się to program pokazałby błąd zawierający miejsce wystąpienia błędu oraz w jaki sposób kod dotarł do tego miejsce czyli tzw. Call Stack. Dla programisty jest to szczególnie ważna informacja, ponieważ może dokładnie odczytać co po kolei wykonywała aplikacja. Oto przykładowy błąd:

Komunikat o błędzie

Pierwsze miejsce wskazane od góry w komunikacie jest miejscem, gdzie aktualnie znajduje się program tj. w pliku Program.cs w linijce 20 wewnątrz funkcji Program.Test(). Wcześniej program wykonywał funkcję Program.Main() w linijce 25 w tym samym pliku. Dalej jest podane jeszcze dużo innych miejsc w kodzie, które opisują w jaki sposób aplikacja została uruchomiona.

Dokładność Algorytmu

Jak jednak można się spodziewać jeśli algorytm operuje na wartościach rzeczywistych to wynik nie zawsze musi być identyczny. Wtedy można sprawdzić czy wyniki dwóch algorytmów nie różnią się o zbyt dużą wartość. Ponownie przyjmujemy, że jeden algorytm daje wyniki wzorcowe, a drugi algorytm jest bardziej efektywny, ale chcemy upewnić się czy zwracane wyniki są poprawne.

W tym przypadku przykładem będzie Algorytm Newtona-Raphsona, który służy do szybkiego obliczania pierwiastka kwadratowego liczby. Chcemy sprawdzić czy algorytm zwraca wyniki z błędem nie większym niż 0.000001 od standardowej funkcji do wyliczania pierwiastka. Algorytm oblicza wynik tak długo, aż kolejne wyniki będą nieznacznie się różnić. Oto kod do przetestowania:

  1. static double sqrtNewtonRaphson(double p, double e) {
  2.   double a = p;
  3.   while (Math.Abs(a - p / a) > e)
  4.     a = (a + p / a) / 2;
  5.   return a;
  6. }

Poniżej została zamieszczona funkcja, która służy do testowania przedstawionego wyżej algorytmu. W pętli dla tych samych danych oblicza wynik zwrócony przez normalne potęgowanie oraz sqrtNewtonRaphson().

  1. static void Test() {
  2.   for (int i = 2; i <= 10; i++) {
  3.     double p = Math.Pow(i, i - 1);
  4.     double error = 0.000001;
  5.     double wynik = sqrtNewtonRaphson(p, error);
  6.     double wzorzec = Math.Sqrt(p);
  7.     Debug.Assert(Math.Abs(wynik - wzorzec) < error);
  8.   }
  9. }

W tym przypadku warunkiem poprawności jest sprawdzenie czy wartość absolutna pomiędzy wynikami jest odpowiednio mała. Algorytm ponownie przeszedł testy bez problemu. Algorytm Newtona-Raphsona zwraca bardzo dokładne wyniki po już kilku krokach. W celu wywołania błędu należałoby zmienić dokładność obliczeń algorytmu.

Zadania

Zadanie 1

Do poniższej funkcji sumaCyfr(), która zwraca sumę cyfr przekazanej liczby napisz funkcję testującą. Argumenty oraz oczekiwane odpowiedzi dowolne. W przypadku błędnej odpowiedzi powinien pojawić się odpowiedni komunikat.

  1. static void Test() {
  2.   int[] argumenty = { 12, 45, 100, 678 };
  3.   int[] wzorzec = { 3, 9, 1, 21 };
  4.   for(int i = 0; i < argumenty.Length; i++) {
  5.     int wynik = sumaCyfr(argumenty[i]);
  6.     Debug.Assert(wynik == wzorzec[i]);
  7.   }
  8. }