Potyczki na froncie, odcinek 3: Odpluskwianie Angulara


Usuwanie błędów jest nieodłączną częścią procesu tworzenia i utrzymania aplikacji.  
Nie da się całkowicie uniknąć powstawania błędów, określanymi po angielsku sympatycznym słowem ‘bug’ (pluskwa, robak), ale możemy je wszystkie, lub przynajmniej większość z nich, eliminować  
w zarodku, jeśli umiejętnie korzystamy z dostępnych narzędzi. Opowiem o tych, z których sam korzystam, i które, moim zdaniem, powinny przydać się każdemu programiście tworzącym aplikacje Angular i nie tylko. 

Generalnie nie możemy liczyć, że dostępne narzędzia automatycznie zlokalizują i całkowicie wyeliminują problem. Wyjątkiem jest zastosowanie linterów takich jak SonarLint lub TSLint,  
które mogą automatycznie sygnalizować lub nawet poprawiać zauważone błędy, ale potrafią to zrobić tylko z niektórymi, najbardziej oczywistymi błędami, dotyczącymi zazwyczaj składni języka. Często jednak kluczowe są błędy wynikające z nieprzewidzenia specyficznych scenariuszy zachowania aplikacji, określonych stanów aplikacji powodujących wystąpienie nieprzewidzianego i w związku  
z tym nieobsługiwanego wyjątku. Szczególne wyzwanie mogą stanowić błędy niegenerujące żadnego wyjątku, ale powodujące niewłaściwe zachowanie aplikacji, zwłaszcza takie błędy, które mają niedeterministyczny charakter, tzn. losową naturę, i w związku z tym ich odtworzenie nie jest możliwe za każdym razem. Przykładem tego rodzaju błędu są tzw. wyścigi, występujące wtedy, gdy nasza aplikacja posiada dwa lub więcej procesów, które powinny zakończyć się w określonej kolejności, i dokładnie taka kolejność została założona przez programistę, i jest wymagana do prawidłowego działania aplikacji. Trudny do zrozumienia i wyeliminowania błąd może pojawić się, gdy z powodu nieprzewidzianych i czasem niezależnych od samej aplikacji przyczyn, owa kolejność ulegnie zmianie. Efektywne lokalizowanie i rozwiązywanie tego typu błędów wymaga przede wszystkim zrozumienia systemu, doświadczenia, praktycznej wiedzy wspomaganej odpowiednim zestawem narzędzi. 

Od czego zacząć? 

Różne typy błędów wymagają odmiennego podejścia. Zwykle jednak pierwsza rzecz, którą robię przy diagnozowaniu aplikacji, to wciśnięcie w przeglądarce internetowej klawisza F12 w celu uruchomiania tzw. ‘Narzędzi dla programistów’ (ang. ‚Developer Tools’) i wybranie zakładki Console, dzięki czemu mogę zobaczyć log z różnego typu komunikatami sygnalizującymi nieprawidłowości  
w działaniu aplikacji. Osobiście najczęściej korzystam z wiodącej przeglądarki Chrome i na niej skupiam się w tym artykule, ale w większości innych popularnych przeglądarek istnieją również wbudowane narzędzia dla programistów, które można uruchomić w identyczny sposób. 

Jeśli nasza aplikacja zachowuje się niewłaściwie, jest szansa, że w komunikatach błędów konsoli przeglądarki znajdziemy podpowiedź, zwłaszcza gdy mamy do czynienia z klasycznym błędem  
‘null reference exception’. Czasem jednak może to przypominać przysłowiowe szukanie igły w stogu siana. Nie wiadomo, czy znajdziemy jakiekolwiek informacje istotne w kontekście naszego problemu, natomiast prawdopodobnie natkniemy się na wiele innych komunikatów, niepowiązanych z diagnozowanym błędem. Jeśli nasza aplikacja nie generuje dużej ilości komunikatów w konsoli, to jest to pozytywny wyjątek od reguły, którą widać, gdy uruchomimy konsolę np. na którymkolwiek z popularnych portali internetowych, logujących mnóstwo komunikatów za każdym razem po otwarciu lub odświeżeniu strony. 

Zazwyczaj szukanie błędów w konsoli nie wystarcza, by dokładnie zdiagnozować problem. Trzeba prześledzić stan aplikacji, tj. zachowanie się poszczególnych podejrzanych zmiennych, aby na tej podstawie ustalić istotę problemu. 

Logowanie stanu aplikacji 

Najprostszy i najbardziej uniwersalny sposób pozwalający sprawdzać wartość zmiennej w dowolnym momencie i wyświetlić ją w oknie dowolnej przeglądarki na dowolnym urządzeniu to wykorzystanie Javascriptowej metody alert(text: string). Funkcja ta powoduje wstrzymanie działania aplikacji  
i wyświetlenie zdefiniowanego komunikatu w oddzielnym oknie. W tym komunikacie możemy umieścić wartość zmiennej lub wielu zmiennych typu tekstowego lub liczbowego. 

Znacznie częściej niż z metody alert korzystam jednak z możliwości logowania komunikatów w konsoli przeglądarki, w szczególności z metody console.log(), z kilku powodów. Przede wszystkim, za pomocą console.log możemy wyświetlić zawartość zmiennych dowolnego typu, w tym złożonych obiektów. Poza tym, możemy zalogować zawartość zmiennej w kilku różnych miejscach aplikacji i potem zobaczyć obok siebie w oknie konsoli wszystkie wyniki, by móc je porównać. 

Choć zazwyczaj wystarcza console.log, warto wiedzieć o paru innych użytecznych metodach obiektu console, takich jak: 

  • console.table(array) – wyświetla tablicę zmiennych w formie przejrzystej tabelki, 
  • console.time(label: string), console.timeEnd(label: string) – włącza stoper o danym identyfikatorze i następnie wyłącza go, logując na konsoli czas, jaki upłynął od włączenia. 

Przykładowo, uruchomienie poniższego kodu skutkuje efektem widocznym na poniższym obrazku: 

Powyższe rozwiązanie jest stosunkowo proste, ale ma, podobnie jak ‘alert’, jedną zasadniczą wadę – wymaga podwójnej modyfikacji kodu – najpierw musimy dodać metody logujące interesujące  
nas wartości, a potem, po skończeniu debuggowania wypadałoby owe wszystkie zmiany usunąć. 

VS Code debugger 

Istnieje altenatywna metoda śledzenia stanu zmiennych, opierająca się na debuggerze wbudowanym w Visual Studio Code. Po odpowiednim skonfigurowaniu edytora możemy postawić breakpointy  
poszczególnych liniach kodu, aby następnie prześledzić wartości interesujących nas zmiennych w tych i kolejnych wierszach. 

Aby skorzystać z VS Code debuggera, przykładowo w połączeniu z przeglądarką Chrome, należy: 

1) zainstalować wtyczkę ‘Debugger for Chrome’, 

2) zapisać projekt jako workspace – File -> Save workspace as…

3) upewnić się, że posiadamy w głównym folderze naszego projektu folder .vscode, a w nim plik launch.json zawierający konfigurację debuggera. Dla standardowego projektu stworzonego  
przez Angular CLI ów plik może wyglądać następująco: 

Następnie wystarczy uruchomić aplikację komendą ng serve oraz rozpocząć debuggowanie klawiszem F5. 

Podobnie jak p.. przy debuggowaniu w Visual Studio kodu w C#, możemy ustawiać breakpointy myszką lub klawiszem F9, aby działanie aplikacji zatrzymało się w wybranym miejscu. Działają również m.in. klawisze F10 F11 umożliwiające przejście do kolejnej instrukcji. Jeśli wolimy klikać myszką, możemy wykorzystać w intuicyjny sposób automatycznie pojawiające się podręczne menu: 

W oknie WATCH możemy z kolei obserwować aktualną w danym momencie wartość zmiennej, której nazwę wcześniej tam wpisaliśmy. 

Całe okno VS Code przy otwartej sesji debuggowania wygląda następująco:  

Co ciekawe, sesją debuggowania możemy sterować nie tylko z poziomu okna VS Code, lecz także z poziomu okna przeglądarki. Możemy w Developer Tools, w zakładce Sources podejrzeć linię kodu  
na której aplikacja została wstrzymana, 

widzimy aktualną wartość zmiennych,  

a także menu podobne do tego z VS Code. 

Podsumowując, wykorzystanie debuggera wbudowanego w środowisko IDE wymaga poświęcenia paru chwil na wstępną konfigurację, ale dzięki temu potem debuggowanie staje się wygodniejsze.  
Im bardziej skomplikowany jest nasz przypadek, im więcej kodu i im więcej zmiennych musimy przenalizować, tym bardziej odczujemy korzyści z tego typu rozwiązania. 

API 

Działanie naszej frontendowej aplikacji jest zwykle zależne od tego, jakie dane dostaje ona z backendu. Niejednokrotnie problem wykryty na frontendzie jest jedynie skutkiem nieprawidłowości w działaniu backendu lub w komunikacji na linii frontend – backend. Przy tego typu problemach ponownie bardzo przydają się wbudowane w przeglądarkę narzędzia dla programistów, które oczywiście nie pozwolą nam bezpośrednio na rozwiązanie błędów w innych warstwach systemu, ale umożliwiają dokładne ustalenie, jakie zapytania są przez naszą aplikację frontendową wysyłane i jakie dane, po jakim czasie, otrzymuje ona w odpowiedzi. Służy do tego zakładka Network

Jeśli skupiamy się na analizie asynchronicznych zapytań do naszego backendu, aby nie zgubić  
się w gąszczu danych, warto odfiltrować wszystkie innego typu zapytania, klikając XHR

Jak widać na powyższym obrazku, łatwo zauważyć zapytania zakończone niepowodzeniem.  
Są one oznaczone kolorem czerwonym i zawierają stosowną informację w kolumnie status. 

Klikając prawym przyciskiem myszy na dane zapytanie, możemy podejrzeć treść nagłówków zapytania oraz pełną treść odpowiedzi. Możemy też sprawdzić dokładnie, w jakim czasie dane zapytanie zostało wysłane i w jakim czasie została odebrana odpowiedź. 

Przydaje się również możliwość ponowienia wybranego zapytania (replay XHR) w sytuacji,   
gdy równolegle debuggujemy nasz backend. 

CSS 

Błędy nie zawsze są związane z logiką i przepływem danych w aplikacji. Czasem problemem  
jest wyłącznie niewłaściwy wygląd określonych elementów na stronie. Jeśli musimy ustalić, kiedy  
i dlaczego wygląd aplikacji się psuje i które konkretnie ustawienia są za to odpowiedzialne, ponownie z pomocą przychodzą nam wspomniane wcześniej narzędzia dla programistów. 

Po wybraniu zakładki Elements i kliknięciu stosownej ikonki lub wykorzystaniu skrótu klawiaturowego Ctrl+Shift+C wystarczy najechać myszką na dany element strony, aby wyświetlić kod odpowiedzialny za jego stylowanie. 

Co więcej, po kliknięciu na danym elemencie ów kod można modyfikować bezpośrednio w przeglądarce, aby testując w ten sposób potencjalne rozwiązania, widzieć od razu ich efekty. 

Cache, Cookies, LocalStorage 

Zakładka Application narzędzi DevTools pozwala również na podgląd cennych w niektórych sytuacjach informacji o podręcznej pamięci przeglądarki, w tym np. Cookies oraz LocalStorage. Można również  
w prosty sposób wyedytować lub skasować poszczególne wpisy. 

Augury 

Kolejną zaletą konsoli Chrome DevTools jest jej rozszerzalność. Istnieją wtyczki umożliwiające dodanie interesujących funkcjonalności. Jedna z nich, dostępna dla przeglądarek Chrome i Firefox, nazywa się Augury i może być przydatna dla każdego programisty Angulara tym bardziej, z im bardziej złożonymi aplikacjami ma on do czynienia. 

Rozszerzenie to dodaje nową zakładkę w narzędziach dla programistów. Po jej wybraniu jesteśmy  
w stanie m.in. podejrzeć mapę zależności pomiędzy komponentami naszej aplikacji.  

Możemy również wyświetlić stan wybranego komponentu, 

a także zlokalizować komponent odpowiedzialny za wyświetlanie danego elementu na stronie. 

Dzięki temu rozszerzeniu możemy zatem szybciej zrozumieć strukturę kodu diagnozowanej aplikacji napisanej w Angularze i łatwiej zlokalizować podejrzane miejsca w kodzie. 

Architektura 

Wspomniane wyżej narzędzia dają ogromne możliwości diagnozy różnego rodzaju błędów  
w aplikacjach Angular. Fundamentem strategii walki z błędami takich aplikacji powinny być jednak  
nie one, lecz przede wszystkim dobrze przemyślana architektura, która pozwoli zapobiec wielu błędom, a w przypadku tych niemożliwych do uniknięcia znacznie ułatwi diagnozę i rozwiązanie. 

Warto stosować wszystkie znane dobre praktyki poprawiające przejrzystość kodu i od początku zapewnić adekwatny do złożoności aplikacji sposób przepływu danych. W przypadku rozbudowanych aplikacji należy przemyśleć zastosowanie scentralizowanego zarządzania stanem, takiego jak NGRX, odpowiednika REACTowego Reduxa. W rezultacie oprogramowanie zmian stanu będzie wymagało dodatkowych nakładów, ale w zamian otrzymamy znacznie lepszą kontrolę nad naszym rosnącym ekosystemem, synchronizację między komponentami oraz możliwość skorzystania z kolejnego bardzo cennego narzędzia: Redux DevTools

Przy zastosowaniu rozwiązania tupu Redux wszystkie zmiany stanu aplikacji odbywają  
się w sformalizowany sposób, za pomocą ustalonych eventów, a aktualny stan jest przechowywany  
w jednym, przeznaczonym do tego miejscu. Wszystkie zmiany mogą być logowane w taki sposób,  
że potem w przeglądarce, za pomocą wtyczki Redux Devtools możemy dokładnie prześledzić ich ciąg, a nawet możemy z naszą aplikacją podróżować w czasie, przewijając ją do określonego stanu  
z przeszłości. 

Podsumowanie 

Jeśli jeszcze tego nie robicie, zachęcam do korzystania z wyżej opisanych technik diagnozowania błędów. Mam nadzieję, że okażą się w Waszych potyczkach równie pomocne, jak w moim przypadku. Jeśli pominąłem coś, o czym Waszym zdaniem warto byłoby tu dodatkowo wspomnieć, zachęcam  do dodania komentarza. 

Linki 

https://developer.mozilla.org/pl/docs/Web/API/Console – metody console 

https://code.visualstudio.com/docs/editor/debugging#_launch-configurations – debuggowanie w VS Code 

https://augury.rangle.io/ – wtyczka Augury dla przeglądarek Chrome I Firefox 

https://ngrx.io/ – NGRX 

https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=pl – Redux Devtools 


Piotr Zyśk

O Piotr Zyśk

Od 2019 roku jako programista fullstack w Atenie zgłębiam głównie technologie .Net i Angular. Wcześniej przez kilkanaście lat zajmowałem się szeroko pojętym wsparciem administracyjno-programistycznym w dla dużej sieci call-center. W wolnym czasie lubię wykorzystywać swój komputer na różne sposoby, np. jako studio do tworzenia muzyki lub urządzenie do symulacji rajdów i wyścigów samochodowych. Często również można spotkać mnie w podolsztyńskich lasach na rowerze, z aparatem fotograficznym.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Komentarz do “Potyczki na froncie, odcinek 3: Odpluskwianie Angulara

  • Avatar
    Aneta

    Hej. Świetnie przygotowany post. Dużo ważnych informacji, które z pewnością mi się przydadzą bo średnio radzę sobie z tym problemem. Bardzo podoba mi się spora ilość screenów, które są bardzo pomocne 🙂 Pozdrawiam