Testowanie kodu i obsługa błędów JavaScript.

Code secured

Poziom zaawansowany

Debug it! W tym artykule zajmiemy się testowaniem kodu i obsługą błędów w JavaScript.

Obsługa błędów JavaScript

Na początek trzeba by powiedzieć kilka słów o potrzebie testowania.

Testować trzeba wszystko! Najlepiej na każdym etapie tworzenia; im wcześniej wyłapiemy błędy, tym lepiej dla oprogramowania, które w pocie czoła tworzymy.

A jako twórcy musimy dbać o jakość kodu! Dobry kod to nie tylko kod przetestowany, ale również taki, który reaguje na trudne do przewidzenia sytuacje. Mówimy wtedy o obsłudze wyjątków, co JavaScript również wspiera.

Podstawowa obsługa błędów w JavaScript

Na początek prosty przykład: użycie onerror. Załóżmy, że przeglądarka spróbuje załadować obrazek z pliku, który nie istnieje.

Przykład – zdefiniowanie reakcji na określony błąd:

<img src="not_existing_foobar.png" 
  onerror="alert('Wystąpił błąd')" />

Podczas nieudanej próby załadowania obrazka otrzymamy obsługę błędu (tutaj alert z informacją).

Idąc dalej. Przykład poniżej jest dość przydatny – informuje nas o tym, co poszło nie tak w naszym kodzie, gdy wystąpi jakiś błąd.

Przykład – przechwycenie i obsługa dowolnego błędu, np. nieistniejącej funkcji:

window.onerror = function(sMessage, sUrl, sLine) {
    alert("Błąd:\n" + sMessage + "\nURL: " + sUrl + "\n Numer wiersza: " + sLine);

    return true;
}

// wywołaj nieistniejącą funkcję:
nonExistentFunction();

Obiekt typu Error

Obsługa błędów w JavaScript daje nam różne narzędzia. Jednym z nich jest tworzenie i rzucanie (throw) obiektów stworzonych do tego celu (Error). Obiekt taki przechowuje bardziej szczegółowe informacje dotyczące błędów.

Przykład – tworzenie obiektu typu Error w celu obsługi wyjątku:

function divide(iNum1, iNum2) {
  if (arguments.length != 2) {
    throw new Error("divide() wymaga dwóch argumentów.");
  }
  else if (typeof iNum1 != "number" || typeof iNum2 != "number") {
    throw new Error("divide() wymaga dwóch argumentów liczbowych");
  }
/*
  else if (iNum2.valueOf() == 0) {
    throw new Error("Nie dziel przez zero");
  }
*/
  return iNum1.valueOf() / iNum2.valueOf();
}

alert(divide("a"));
// alert(divide(2, 0));
// alert(divide("a", 0));

Bardziej szczegółowy opis obiektu Error na stronach Developer Mozilla.

Konstrukcja try… catch… finally

Konstrukcja tego typu występuje w wielu językach obiektowych, a służy do obsługi wyjątków.

W bloku try umieszczamy kod, którego działanie może zakończyć się w nieprzewidywalny sposób.

W bloku catch umieścimy kod obsługi wyjątku, który tu przechwycimy.

Natomiast w opcjonalnym bloku finally – kod, który wykona się niezależnie – zawsze po przetworzeniu bloku try i ewentualnie catch.

Przykład – podstawowe użycie try / catch w celu obsługi wyjątku:

try {
    window.nonExistentFunction();
    alert("Metoda wykonana.");
} catch (oException) {
    alert("Wystąpił wyjątek: " + oException.message);
} finally {
    alert("Koniec instrukcji try… catch");
}

Możliwe jest nawet tworzenie zagnieżdżeń.

Przykład – zagnieżdżanie try / catch:

try {
    eval("a ++ b"); // instrukcja powoduje błąd
} catch (oException) {
    alert("Wystąpił wyjątek.");
    try {
        // instrukcja powoduje błąd
        var aErrors = new Array(10000000000000000000000);
        push(exception);
    } catch (oException2) {
        alert("Wystąpił kolejny wyjątek.");
    }
} finally {
    alert("Koniec rozbudowanego przykładu");
}

Poniżej przykład jeszcze mocniej związany z poruszanym wcześniej OOP. Pod lupą jest teraz użycie wspomnianego już obiektu typu Error.

W tym kodzie określamy typ obiektu błędu (rzuconego w wyjątku), tzn. jego instancji. Robimy to w celu podjęcia działania odpowiedniego dla danego typu.

Przykład – badanie typu błędu Error:

try {
    foo.bar();
} catch (e) {
    if (e instanceof EvalError) {
        alert(e.name + ": " + e.message);
    else if (e instanceof RangeError) {
        e.name + ": " + e.message);
    }
    // ... itd
}

Jak na JavaScript to całkiem elegancki sposób.

Przyjrzyjmy się teraz instrukcji throw.

Instrukcja throw wywołuje wyjątek. Jako argument określamy wyrażenie zawierające wartość, która ma zostać wywołana.

Przykłady użycia instrukcji throw:

throw "Error2";
throw 47;
throw true;
throw { toString: function() { return "Jestem obiektem!"; } };

Asserts – asercje w JavaScript

Asercje to w skrócie konstrukcje umieszczane w kodzie, poprzez które programista zakłada prawdziwość wyrażenia, co przydatne jest w sprawdzaniu oprogramowania pod kątem luk i odporności na błędy. W JavaScript możemy za symulować użycie asercji.

Ten przykład używa funkcji assert() do wyrzucania własnych błędów JavaScript:

function assert(bCondition, sErrorMessage) {
    if (!bCondition) {
        throw new Error(sErrorMessage);
    }
}

function divide(iNum1, iNum2) {
    assert(arguments.length == 2, 
         "divide() wymaga podania dwóch argumentów");

    assert(typeof iNum1 == "number" && typeof iNum2 == "number",
        "divide() wymaga dwóch argumentów będących liczbami");

    // ... obsługa innych przypadków ...

    return iNum1.valueOf() / iNum2.valueOf();
}

alert(divide("a"));

Użycie konsoli (oraz debuggery i profilery)

No koniec słowo o użyciu konsoli w celu testowania kodu (w przeglądarkach wspierających takie rozwiązanie).

Przykład:

var ss = "Test";
console.log(ss);

Zwykle (chyba że są ku temu określone powody) należy pamiętać o wyłączaniu takich instrukcji z kodu produkcyjnego.

Większość z nas zna, i zapewne często używa genialnego narzędzia, jakim jest Firebug. Aż z niedowierzaniem wspominam czasy, gdy pisało się kody bez użycia tego narzędzia.

Firebug jest związany z Firefox’em, ale inne przeglądarki również dostarczając programiście podobnych rozwiązań. Są one nieocenione przy poszukiwaniu przyczyn problemów w kodzie JavaScript, HTML czy CSS.

Firebug:
Firebug

Narzędzia developerskie w Google Chrome:
Google Chrome Debug

W Safari:
Safari Debug

W przeglądarce Opera:
Opera Debug

A nawet IE:
IE Debug

Czasem, gdy wykonuje się testy cross-browser i coś nie gra, nie sposób nie odpalić tych narzędzi.

Podsumowanie

Mam nadzieję że ta mała garść porad pomoże komuś w pisaniu jeszcze lepszego kodu, a przede wszystkim pozwoli spojrzeć szerzej na zagadnienie, jakim jest obsługa błędów JavaScript.

Za jakiś czas opiszemy więcej ciekawych narzędzi, jakimi może wspomóc się Web Developer, m.in: EcmaUnit, Selenium, JSLint, i inne.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem, początkujący bloger. Pasjonat programowania, nowych technologii, e-commerce, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook1Tweet about this on TwitterShare on Google+2Share on LinkedIn1Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.