Testy jednostkowe – czy warto?

Myślę, że każdy w mniejszym lub większym stopniu miał styczność z hasłem „testy jednostkowe”, „unit tests” (UT). Dla niektórych być może jest to strata czasu na niepotrzebne rzeczy. Moim zdaniem wykorzystanie testów jednostkowych w projekcie ma pozytywny wpływ na ostateczny rezultat. Oczywiście, zastosowanie testów jednostkowych ma swoje pozytywne i negatywne strony.

Bez dłuższych rozmyślań – wymagają poświęcenia czasu, którego często nam brakuje. Zazwyczaj gonią nas terminy, czy to dostarczenia całej funkcjonalności, czy po prostu sprintu. Napisanie testów wymaga czasu, bo to wciąż pisanie kodu, które wymaga choćby odrobiny zastanowienia.
Kolejną kwestią jest sama treść testów. Testy jednostkowe powinny sprawdzać jak najmniejszą logicznie powiązaną część funkcjonalności aplikacji jak np. wyliczenie sumy pozycji na fakturze. Czasami logika stojąca za funkcjonalnością jest na tyle skomplikowana, że jest ją trudno przetestować przy pomocy UT, zwłaszcza w projekcie, który istnieje od lat. Być może prościej będzie przetestować różne przypadki manualnie zamiast spędzać godziny na ewentualnej reorganizacji kodu.

Przejdźmy do pozytywnych stron. Gdy postanowimy sobie, że każda nowa funkcjonalność powinna zostać pokryta testami jednostkowymi będziemy pisali kod w taki sposób, aby był łatwo testowalny. Takie podejście umożliwi nam takie rozbicie funkcjonalności aby było możliwe łatwe przetestowanie najmniejszego jej fragmentu w jak najłatwiejszy sposób.
Pisanie testów jednostkowych do kodu, który należy do starych funkcjonalności tak naprawdę możemy pominąć. Dla ułatwienia można uznać stary, nietestowany kod za zewnętrzną bibliotekę i testować funkcje, z których korzystamy przy pisaniu nowych części aplikacji. Inną opcją jest pisanie testów w momencie otrzymania zgłoszenia o błędzie – by powtórzyć błędogenny scenariusz. Mamy w ten sposób część oprogramowania i tak zostanie pokryta testami, bo bądźmy szczerzy – która firma rozwijająca projekt od jakiegoś czasu jest w stanie przerwać jego rozwój na jakiś czas by pokryć testami całą aplikację?

W poprzednich akapitach pisałem, że testy jednostkowe wymagają poświęcenia dodatkowego czasu – jest to prawda. Zwróćmy jednak uwagę na to jak wygląda rozwój oprogramowania. Programiści kończą rozwój pewnej części aplikacji, następnie przekazują ją do testerów. Tester sprawdza co programiści wymyślili i ewentualnie zgłasza błędy. Części z nich, udałoby się uniknąć gdyby były stosowane testy jednostkowe. Tester zamiast sprawdzać po raz n-ty czy podstawowe scenariusze testów są spełnione mógłby po pierwszej weryfikacji sprawdzić inne opcje, które nie przyszły programiście do głowy. Można się także pokusić o pisanie testów do zgłoszonych błędów. Jeśli nasza poprawka zostanie zaakceptowana to nasz test jednostkowy będzie mógł w przyszłości służyć do sprawdzania, czy kolejne zmiany nie zaburzyły działania aplikacji. Wniosek z tego – oszczędzamy czas testerów, co skutkuje oszczędnością pieniędzy.

Krótko podsumowując – pomimo „przepalania” czasu na testy jednostkowe możemy ostatecznie go zyskać. Części błędów możemy uniknąć, a część znacznie szybciej naprawić i zadbać by więcej nie wróciły. Dzięki temu zaoszczędzimy czas naszego działu testów.
Kod, który nie został podczas pisania pokryty testami może zostać chociaż częściowo o nie uzupełniony – wystarczy zadbać o to by podczas naprawiania defektów napisać testy potwierdzające jego występowanie przed pracami nad poprawką. Tak więc nie taki diabeł straszny jak go malują. Moim zdaniem pomimo kosztów używania warto wykorzystywać testy jednostkowe.

It’s (almost) alive!

W piątkowym wpisie opisałem pokrótce bibliotekę JDOM Parser. Jak już wspomniałem wykorzystałem ją przy okazji przebudowy zbioru wzorców. Było to konieczne, ponieważ chciałem powiększyć zbiór wzorców, które wykorzystuję do korelacji. Po gruntownym rozszerzeniu i przebudowie zbioru danych uruchomiłem algorytm rozpoznawania znaków.

Dla niektórych paragonów możliwe było już zrozumienie zeskanowanej treści. Niestety nadal brakuje wzorców dla znaków interpunkcyjnych oraz rozpoznawania odstępów między wyrazami – będę nad tym pracował.

Mimo wszystko mogę powiedzieć: It’s (almost) alive!

(więcej…)

Prosty przepis na obsługę dokumentów XML – JDOM Parser

Przy okazji prac nad wzorcami do rozpoznawania znaków natworzyłem masę plików z obrazkami zawierającymi znaki. Nie chciałem wczytywać każdego pliku z osobna, więc postanowiłem utworzyć zbiór w pliku XML. Jako, że nie orientuję się zbyt dobrze w ekosystemie Javowym wyruszyłem na łowy – wujek Google był moim przewodnikiem. Podczas wędrówki w otchłaniach „internetów” natrafiłem na opis biblioteki JDOM. Zagłębiłem się w opis i przykłady – spełniała moje niewygórowane wymagania. Miała być zwyczajnie prosta w obsłudze.

Utworzenie nowego dokumentu XML sprowadza się do wywołania dwóch linijek, które tak naprawdę można było skrócić do jednej.

Dodawanie atrybutów odbywa się przy użyciu funkcji setAttribute(…):

Jeśli chcielibyśmy dodać jakąś zawartość do elementu wystarczy wywołać addContent(…).

 

W analogiczny sposób dodajemy gotowy element do jego elementu nadrzędnego, czyli np..:

Zapis do pliku nie jest moim zdaniem skomplikowany. Wystarczy wywołać:

 

Prawda, że proste?

Odczyt danych z pliku jest na podobnym poziomie trudności:

Zamiast pliku możemy także podać np. URL lub strumień danych.

Po otwarciu dokumentu pozostaje dobrać się do jego zawartości. Wystarczy pobrać „korzeń” (root) dokumentu przy pomocy funkcji getRootElement() wywoływanej na dokumencie.

W moim konkretnym przypadku dobrałem się do wszystkich potomków elementu nadrzędnego wywołaniem:

 

Następnie pobrałem atrybuty potomka przy pomocy funkcji getAttributeValue(final String attname), a zawartość getContent(final int index).

Przykładowe wykonanie:

 

 

Strona biblioteki: http://www.jdom.org/

 

PS. To jedna z tych sytuacji w których użycie słowa „potomek” w sformułowaniach jest bezpieczniejsze niż „dziecko” 😉

Rysowanie po obiekcie typu Mat

Dzisiejszy post będzie powiązany z reorganizacją kodu, który powstał do tej pory. Podczas rozwijania projektu naszła mnie myśl, że wypadałoby rozdzielić logikę stojącą za przetwarzaniem zawartości obrazu (wykrywanie linii oraz znaków) od tej, która stoi za jego przekształceniami takimi jak binaryzacja lub zmiana palety kolorów na skalę szarości. Tak powstał interface ImageContentOperations i implementująca go klasa DefaultImageContentOperations. Metody wchodzące w ich skład operują na obrazie zapisanym w obiekcie typu org.opencv.core.Mat. Zmiana ta wymusiła zmianę sposobu rysowania histogramów.

(więcej…)