Testowanie i Ciągła Integracja w Projektach Java Enterprise Edition



Podobne dokumenty
Testowanie oprogramowania. Testowanie oprogramowania 1/34

Usługa: Testowanie wydajności oprogramowania

REFERAT PRACY DYPLOMOWEJ

Budowa aplikacji webowej w oparciu o Maven2 oraz przykłady testów jednostkowych. Wykonał Marcin Gadamer

Program szkolenia: Continuous Integration i Git

Web frameworks do budowy aplikacji zgodnych z J2EE

Instalacja SQL Server Express. Logowanie na stronie Microsoftu

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery

Testowanie aplikacji mobilnych na platformie Android - architektura, wzorce, praktyki i narzędzia

Architektury Usług Internetowych. Laboratorium 2. Usługi sieciowe

Platformy Technologiczne

Temat: Ułatwienia wynikające z zastosowania Frameworku CakePHP podczas budowania stron internetowych

Analiza i projektowanie oprogramowania. Analiza i projektowanie oprogramowania 1/32

Galileo - encyklopedia internetowa Plan testów

Usługi analityczne budowa kostki analitycznej Część pierwsza.

Grzegorz Ruciński. Warszawska Wyższa Szkoła Informatyki Promotor dr inż. Paweł Figat

Baza danych sql. 1. Wprowadzenie

Czym jest Java? Rozumiana jako środowisko do uruchamiania programów Platforma software owa

Problemy optymalizacji, rozbudowy i integracji systemu Edu wspomagającego e-nauczanie i e-uczenie się w PJWSTK

Programowanie obiektowe - 1.

Szczegółowy opis przedmiotu umowy. 1. Środowisko SharePoint UWMD (wewnętrzne) składa się z następujących grup serwerów:

Szablon Planu Testów Akceptacyjnych

Michał Olejnik. 22 grudnia 2009

Komputerowe Systemy Przemysłowe: Modelowanie - UML. Arkadiusz Banasik arkadiusz.banasik@polsl.pl

Wykład 1 Inżynieria Oprogramowania

Tester oprogramowania 2014/15 Tematy prac dyplomowych

JUnit TESTY JEDNOSTKOWE. Waldemar Korłub. Platformy Technologiczne KASK ETI Politechnika Gdańska

Uniwersytet Łódzki Wydział Matematyki i Informatyki, Katedra Analizy Nieliniowej. Wstęp. Programowanie w Javie 2. mgr inż.

Dokument Detaliczny Projektu Temat: Księgarnia On-line Bukstor

Maciej Oleksy Zenon Matuszyk

Całościowe podejście do testowania automatycznego dla programistów. (TDD, BDD, Spec. by Example, wzorce, narzędzia)

Produktywne tworzenie aplikacji webowych z wykorzystaniem Groovy i

<Nazwa firmy> <Nazwa projektu> Specyfikacja dodatkowa. Wersja <1.0>

Programowanie MorphX Ax

Zarządzanie i realizacja projektów systemu Microsoft SharePoint 2010

Dokument Detaliczny Projektu

Wykład I. Wprowadzenie do baz danych

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

PLAN ZARZĄDZANIA KONFIGURACJĄ OPROGRAMOWANIA PROJEKT <NAZWA PROJEKTU> WERSJA <NUMER WERSJI DOKUMENTU>

Rozwiązanie Compuware dynatrace

Szczegółowa specyfikacja funkcjonalności zamawianego oprogramowania.

Krótka Historia. Co to jest NetBeans? Historia. NetBeans Platform NetBeans IDE NetBeans Mobility Pack Zintegrowane moduły. Paczki do NetBeans.

PHP: bazy danych, SQL, AJAX i JSON

INŻYNIERIA OPROGRAMOWANIA TESTOWANIE SYSTEMOWE

Załącznik 1 instrukcje instalacji

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

Wzorce projektowe i refaktoryzacja

Procesowa specyfikacja systemów IT

Testowanie oprogramowania

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Usługa: Audyt kodu źródłowego

Testowanie według modelu (MBT) Stowarzyszenie Inżynierii Wymagań wymagania.org.pl

Plan. Wprowadzenie. Co to jest APEX? Wprowadzenie. Administracja obszarem roboczym

AUREA BPM HP Software. TECNA Sp. z o.o. Strona 1 z 7

Wprowadzenie do projektu QualitySpy

Programowanie obiektowe

Pytania i wyjaśnienia treści Specyfikacji Istotnych Warunków Zamówienia

Wykład 7. Projektowanie kodu oprogramowania

Jarosław Kuchta Dokumentacja i Jakość Oprogramowania. Wymagania jakości w Agile Programming

Zwinna współpraca programistów i testerów z wykorzystaniem BDD i. by Example (JBehave/Spock/SpecFlow)

Projekt dotyczy stworzenia zintegrowanego, modularnego systemu informatycznego wspomagającego zarządzanie pracownikami i projektami w firmie

Spring Framework - wprowadzenie i zagadnienia zaawansowane

Testowanie I. Celem zajęć jest zapoznanie studentów z podstawami testowania ze szczególnym uwzględnieniem testowania jednostkowego.

Warstwa integracji. wg. D.Alur, J.Crupi, D. Malks, Core J2EE. Wzorce projektowe.

Tworzenie oprogramowania

Analiza i projektowanie obiektowe 2016/2017. Wykład 10: Tworzenie projektowego diagramu klas

Wstęp do Informatyki. Klasyfikacja oprogramowania

Dokument Detaliczny Projektu

Hurtownie danych - przegląd technologii

Wprowadzenie do metodologii modelowania systemów informacyjnych. Strategia (1) Strategia (2) Etapy Ŝycia systemu informacyjnego

Automatyzacja testowania oprogramowania. Automatyzacja testowania oprogramowania 1/36

Porównanie metod i technik testowania oprogramowania. Damian Ryś Maja Wojnarowska

Zdalne monitorowanie i zarządzanie urządzeniami sieciowymi

Projektowanie obiektowe Wzorce projektowe. Gang of Four Strukturalne wzorce projektowe (Wzorce interfejsów)

Bezpieczeństwo systemów komputerowych

Kurs programowania. Wykład 12. Wojciech Macyna. 7 czerwca 2017

Etapy życia oprogramowania

IO - Plan wdrożenia. M.Jałmużna T.Jurkiewicz P.Kasprzyk M.Robak. 5 czerwca 2006

AUREA BPM Oracle. TECNA Sp. z o.o. Strona 1 z 7

Web frameworks do budowy aplikacji zgodnych z J2EE. Jacek Panachida

Model referencyjny doboru narzędzi Open Source dla zarządzania wymaganiami

Zarządzanie testowaniem wspierane narzędziem HP Quality Center

PLAN WYNIKOWY PROGRAMOWANIE APLIKACJI INTERNETOWYCH. KL IV TI 6 godziny tygodniowo (6x15 tygodni =90 godzin ),

Dokumentacja wstępna TIN. Rozproszone repozytorium oparte o WebDAV

Zofia Kruczkiewicz - Modelowanie i analiza systemów informatycznych 2

Win Admin Replikator Instrukcja Obsługi

Rozdział 3. ROZWÓJ APLIKACJI CENTRALNEJ

Rok szkolny 2015/16 Sylwester Gieszczyk. Wymagania edukacyjne w technikum. ADMINISTROWANIE BAZAMI DANYCH kl. 4c

Testowanie oprogramowania. Piotr Ciskowski

Uniwersytet Mikołaja Kopernika. Wydział Matematyki i Informatyki Wydział Fizyki, Astronomii i Informatyki Stosowanej

Diagram wdrożenia. Rys. 5.1 Diagram wdrożenia.

System zarządzający grami programistycznymi Meridius

Automatyczne decyzje kredytowe, siła szybkiego reagowania i optymalizacji kosztów. Roman Tyszkowski ING Bank Śląski S.A. roman.tyszkowski@ingbank.

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Currenda EPO Instrukcja Konfiguracji. Wersja dokumentu: 1.3

Zagadnienia (1/3) Data-flow diagramy przepływów danych ERD diagramy związków encji Diagramy obiektowe w UML (ang. Unified Modeling Language)

Czym jest jpalio? jpalio jpalio jpalio jpalio jpalio jpalio jpalio jpalio

Zapytanie ofertowe

Transkrypt:

UNIWERSYTET JAGIELLOŃSKI W KRAKOWIE Praca magisterska Testowanie i Ciągła Integracja w Projektach Java Enterprise Edition Adam Perlik Pracę wykonano w Zakładzie Technologii Informatycznych pod kierunkiem Dr Katarzyny Grzesiak-Kopeć Wydział Fizyki, Astronomii i Informatyki Stosowanej Kraków 2009

Spis treści Wstęp 6 Motywacja... 6 Cel pracy... 8 Organizacja pracy... 8 Rozdział 1. Testowanie systemów informatycznych 9 1.1. Czym jest testowanie systemów informatycznych?... 9 1.1.1. Testowanie metodą białej skrzynki... 10 1.1.2. Testowanie metodą czarnej skrzynki... 11 1.2. Wybrane typy testów... 12 1.2.1. Testy jednostkowe... 12 1.2.1.1. Wykrywanie błędów... 13 1.2.1.2. Łamanie wymagań... 14 1.2.1.3. Dokumentacja... 14 1.2.2. Testy integracyjne... 14 1.2.2.1. Model z góry na dół... 15 1.2.2.2. Model z dołu do góry... 15 1.2.2.3. Model hybrydowy... 16 1.2.3. Testy funkcjonalne... 16 1.2.4. Test niefunkcjonalne... 17 1.2.4.1. Wydajność... 17 1.2.4.2. Obciążenie... 17 1.2.4.3. Wytrzymałość... 18 1.2.5. Testowalność oprogramowania... 18 1.2.5.1. Operatywność... 18 1.2.5.2. Obserwowalność... 18 1.2.5.3. Sterowność... 19 1.2.5.4. Dekompozycja... 19 1.2.5.5. Prostota... 19 1.2.5.6. Stabilność... 19 1.2.5.7. Zrozumiałość... 19 3

1.3. Ciągła integracja... 20 1.3.1. Utrzymywanie pojedynczego repozytorium kodu... 20 1.3.2. Automatyzacja budowy... 20 1.3.3. Samo-testowanie się systemu podczas budowy... 21 1.3.4. Każda zmiana w repozytorium kodu powinna wyzwalać proces integracji... 21 1.3.5. Częsta synchronizacja kodów źródłowych z repozytorium... 21 1.3.6. Wszyscy widzą co się dzieje... 21 Rozdział 2. Narzędzia wykorzystane do stworzenia platformy testowej 23 2.1. Środowisko programistyczne... 23 2.1.1. Maven... 23 2.1.1.1. Cykl budowy aplikacji... 25 2.1.1.2. Zarządzanie zależnościami... 26 2.1.2. Repozytorium kodu źródłowego Subversion... 28 2.1.3. Eclipse Integrated Development Environment... 28 2.2. Biblioteki programistyczne... 29 2.2.1. Java i Java Enterprise Edition... 29 2.2.2. Springframework... 30 2.2.3. JUnit... 34 2.2.4. Mockito... 35 2.3. Narzędzia testujące... 36 2.3.1. Selenium Remote Control... 36 2.4. SoapUI... 37 2.5. Serwer ciągłej integracji Hudson... 37 2.6. Openfire Server... 38 Rozdział 3. Implementacja platformy testowej 40 3.1. Przykładowa aplikacja Java Enterprise Edition... 40 3.1.1. Wymagania stawiane aplikacji... 40 3.1.2. Architektura systemu... 43 3.1.3. Proces budowy aplikacji... 45 3.2. Pokrycie testami poszczególnych części systemu... 50 3.2.1. Testy jednostkowe... 50 3.2.2. Testy integracyjne... 53 3.2.3. Testy funkcjonalne... 57 3.2.4. Testy wydajnościowe... 59 4

Podsumowanie 61 Rozdział 4. Appendix 63 4.1. Instalacja i uruchomienie platformy testowej... 63 4.1.1. Instalacja maszyny wirtualnej języka Java... 64 4.1.2. Instalacja i konfiguracja repozytorium Subversion... 64 4.1.3. Instalacja serwera Openfire oraz komunikatora PSI... 67 4.1.5. Instalacja i konfiguracja serwera Hudson... 68 4.2. Prezentacja procesu ciągłej integracji... 71 Spis tabel i rysunków 74 Bibliografia 76 5

Wstęp Motywacja W dzisiejszych czasach, w epoce Internetu trudno sobie wyobrazić jakąkolwiek gałąź gospodarki, która nie korzysta z dedykowanych systemów informatycznych. Banki udostępniają swoim klientom obsługę konta przez Internet lub telefon komórkowy, sieci handlowe oferują sprzedaż on-line, która pozwala na robienie zakupów bez wychodzenia z domu. Z perspektywy końcowego użytkownika wspomnianych wyżej aplikacji oferowane przez nie funkcje są proste i oczywiste. Przykładowo na stronie naszego operatora telefonicznego wybieramy opcję Przedłuż Umowę, podajemy swój numer telefonu, wybieramy plan taryfowy wraz z modelem nowego aparatu, zatwierdzamy i drukujemy gotową umowę. W rzeczywistości to, co użytkownik postrzega jako proste i przyjazne jest często bardzo skomplikowanym systemem informatycznym składającym się z setek tysięcy linii kodu źródłowego tworzonego przez wiele osób, nierzadko przez kilka lat. W przytoczonym przeze mnie przykładzie przedłużania umowy z operatorem telefonii komórkowej łatwo można wyróżnić szereg odrębnych aplikacji, których współpraca jest niezbędna do realizacji żądanego procesu biznesowego. Można tu przytoczyć na przykład system CRM zapewniający informacje o kliencie, ERP zapewniający informacje o dostępnych telefonach i planach taryfowych, systemy firm kurierskich dostarczających przesyłki, serwery poczty do wysyłania wymaganych potwierdzeń. Na każdym z etapów takiego procesu istnieje szereg różnych przypadków, które należy obsłużyć. A co się dzieje w przypadku, gdy klient zalega z płatnościami? Co gdy podczas realizacji zamówienia wyczerpał się zapas aparatów wybranych przez klienta? Jak widzimy sam proces biznesowy jest bardzo skomplikowany. Niestety podczas tworzenia oprogramowania realizującego ten proces złożoność wszystkich elementów tylko wzrasta, a każda nawet drobna pomyłka może wywołać poważne konsekwencje. W systemach działających pod 6

dużym obciążeniem nawet minuta niepoprawnego działania aplikacji może wygenerować milionowe straty. W celu minimalizacji błędów w oprogramowaniu wypracowane zostały przeróżne metodologie inżynierii oprogramowania, które usystematyzowały poszczególne etapy rozwoju systemów informatycznych, od zbierania wymagań klienta w stosunku do systemu, aż po ostateczne wdrożenie aplikacji. Dzięki technikom statycznej analizy i budowaniu prototypów dla poszczególnych komponentów można wychwycić błędne założenia w stosunku do systemu, jednak ogromne ryzyko niepoprawnej implementacji poprawnych wymagań wciąż jest obecne. Wykrycie tych pomyłek możliwe staje się dopiero na etapie uruchamiania oprogramowania. Na szczęście na pomoc twórcom systemów informatycznych przychodzi proces testowania. Wraz z rozwojem inżynierii oprogramowania ewoluowały też rodzaje testów i sposoby ich przeprowadzania. Opierając się na doświadczeniach z wcześniejszych projektów, programiści wyznaczali dobre praktyki pozwalające testować coraz więcej aspektów systemu. Niestety, ręczna obsługa wszystkich testów szybko okazała się problemem. W niektórych korporacjach powstały specjalne stanowiska menadżera testów, odpowiedzialnego za okresowe wykonywanie zestawów testów i sporządzanie raportów z ich przebiegu. O ile w metodologiach wytwarzania oprogramowania opartych o model wodospadu, gdzie etap testów jest na samym końcu procesu wytwórczego, nie stanowiło to problemu, to powstanie metodologii dzielących projekt na iteracje, rozwiązanie to było niewystarczające. Ciągła integracja polega na wykonywaniu testów przy każdej zmianie w systemie. W tym celu wykorzystywane są dedykowane narzędzia zintegrowane z repozytoriami kodu źródłowego oraz systemami raportowymi. Systemy ciągłej integracji okresowo sprawdzają repozytoria kodu w poszukiwaniu zmian, w przypadku znalezienia nowych wersji budują projekt, wykonują testy i w przypadku niepowodzenia któregoś z nich, wysyłają raport o tym co się nie udało do użytkowników odpowiedzialnych za wprowadzanie zmian oraz odnotowują niepowodzenie w systemach raportowych. Dzięki zastosowaniu takich narzędzi, błędy mogą być wykrywane na bardzo wczesnym etapie, kiedy to ich naprawienie jest zazwyczaj bardzo proste i nie wymaga dodatkowych nakładów, a tym samym nawet najbardziej skomplikowane funkcjonalności mogą być poprawnie implementowane. 7

Cel pracy Celem niniejszej pracy jest prezentacja nowoczesnego środowiska pracy programisty, którego wykorzystanie do tworzenia systemu informatycznego zbudowanego w oparciu o platformę Java Enterprise Edition pozwala na pełną automatyzację testów tworzonego oprogramowania. Zaproponowane środowisko ciągłej integracji zostało zbudowane w oparciu o narzędzia dostępne za darmo wraz z kodem źródłowym, takie jak: Maven, Hudson, Subversion, JUnit i Selenium Remote Control. Dzięki automatyzacji wykonywania testów przy każdej zmianie wykrytej w systemie zbudowano narzędzie do wczesnego wykrywania błędów. Ze względu na to, że wspomniane powyżej narzędzia stanowią odrębne projekty, nie zawsze tworzone z myślą o ewentualnej integracji w ramach pracy konieczna była również analiza pod kątem możliwości ich współdziałania. Wynikiem tej analizy było powstanie konfiguracji poszczególnych narzędzi tak aby utworzyły pełne środowisko ciągłej integracji. Organizacja pracy W rozdziale pierwszym pracy przedstawiono teorię testowania systemów informatycznych, poszczególne pojęcia związane z testowaniem, możliwe strategie testowania oraz zaprezentowana została koncepcja ciągłej integracji systemu. Rozdział drugi zawiera przegląd narzędzi użytych do stworzenia platformy testowej dla aplikacji napisanej w języku Java Enterprise Edition pozwalającej na automatyzację testów. Rozdział trzeci stanowi główną część pracy. Zawarto w nim opis aplikacji napisanej w oparciu o platformę Java Enterprise Edition, na której wykonywane są testy automatyczne, a także konfigurację poszczególnych komponentów składających się na środowisko ciągłej integracji. 8

Rozdział 1. Testowanie systemów informatycznych Niniejszy rozdział stanowi wprowadzenie do problematyki związanej z testowaniem systemów informatycznych. W kolejnych sekcjach tego rozdziału zdefiniowane zostały następujące zagadnienia: Wyjaśnienie na czym polega testowanie systemu Przedstawienie możliwych strategii testowania Charakterystyka różnych typów testów Opis cech wpływających na testowalność systemu Zdefiniowanie pojęcia ciągła integracja 1.1. Czym jest testowanie systemów informatycznych? Proces testowania systemów informatycznych najczęściej określany jest jako: uruchomienie programu z zamiarem znalezienia w nim błędów [1]. Określenie błąd w odniesieniu do programu komputerowego może mieć wiele znaczeń. Na poniższej liście przedstawiono kilka przykładów błędów możliwych do wykrycia za pomocą testów przeprowadzonych w systemie [1] : błędy w kodzie źródłowym aplikacji, brak implementacji wszystkich wymagań, niepoprawna implementacja wymagań, niewystarczająca wydajność systemu, niestabilność aplikacji. W celu wykrycia poszczególnych typów błędów stosowane są różne rodzaje testów, 9

przeprowadzanych na każdym z etapów rozwoju aplikacji od wczesnych wersji po końcowe fazy przejmowania systemu przez użytkowników. Każdy z testów koncentruje się na sprawdzeniu innej części aplikacji i ma inne cele. Jednak przyglądając się strategii ich wykonywania wyróżnić można dwie metody: białej i czarnej skrzynki. 1.1.1. Testowanie metodą białej skrzynki Metoda białej skrzynki [1] jest sposobem tworzenia i wykonywania testów z perspektywy osoby znającej wewnętrzną strukturę i sposób działania testowanej aplikacji. Testy takie skoncentrowane są na weryfikacji poprawności działania poszczególnych ścieżek przebiegu sterowania w systemie, a także jakości implementacji poszczególnych jednostek systemu. Z tą metoda mocno związane jest pojęcie pokrycia kodu źródłowego [1], które jest miarą tego, do jakiego stopnia tworzone oprogramowanie zostało przetestowane. W celu wyznaczenie wskaźnika pokrycia kodu następujące kryteria są brane pod uwagę: pokrycie funkcji - określające procent przetestowanych funkcji, pokrycie instrukcji określające procent przetestowanych linii kodu źródłowego, pokrycie gałęzi określające procent instrukcji warunkowych przetestowanych zarówno z warunkiem prawda jak i fałsz, pokrycie ścieżek określające procent przetestowanych ścieżek logicznych wiodących przez program. Całościowe przetestowanie aplikacji metodą białej skrzynki powinno dążyć do stuprocentowego pokrycia kodu systemu, a więc takiego, w którym każda jego linijka została wykonana przynajmniej raz. Niestety, często nawet w przypadku bardzo prostych programów jest ono niewykonalne. Może to wynikać zarówno z zakresu danych wejściowych poszczególnych komponentów, jak i z liczby ścieżek możliwych do przejścia w trakcie wykonania danej funkcji. Z tego powodu ta strategia wybierana jest do przetestowania wyłącznie najbardziej kluczowych sekcje programu. Niestety praktyka wybierania kodu do testów, może prowadzić do pomijania elementów, które programistom wydają się być oczywiste i w rezultacie do pominięcia ukrytych w nich błędów. 10

Testy metodą białej skrzynki są najbardziej pomocne we wczesnych fazach rozwoju systemu i ich wyniki pomagają programistom aplikacji w usunięciu błędów w tworzonych komponentach. 1.1.2. Testowanie metodą czarnej skrzynki W testach wykonywanych metodą czarnej skrzynki [1] testowany komponent lub system traktowany jest jako całość, bez dostępu do wewnętrznej struktury kodu programu i szczegółów jego implementacji. Testy te, ze względu na sposób ich przeprowadzania, są też określane jako sterowane danymi. Polegają one na przygotowaniu danych wejściowych i wyjściowych. Dane wyjściowe wprowadzane są do systemu, a odpowiadające im dane wyjściowe, przyrównywane są do tych, które zwróci aplikacja. Oczywiście, w zależności od rodzaju testu, dane wejściowe i wyjściowe przyjmują różne postaci. I tak dla testów funkcjonalnych są to dane wprowadzane do programu za pomocą interfejsu użytkownika, zaś dla testów wydajnościowych będą to czasy odpowiedzi systemu lub liczba obsłużonych transakcji. Testy metodą czarnej skrzynki stosowane są na różnych etapach rozwoju systemu. Jednak im system staje się większy (rośnie skrzynka), tym stają się one bardziej powszechne. Wykonaniem tych testów, w przeciwieństwie do metody białej skrzynki, mogą zajmować się osoby nie związane z implementacją systemu. Atutem wybierania testerów spośród osób nie posiadających wiedzy o programie jest to, że żadna kombinacja danych wejściowych nie jest dla nich oczywista i są one skłonni przetestować te scenariusze, które zostały pominięte przez programistów. Punktem wyjścia do tworzenia testów metodą czarnej skrzynki jest specyfikacja wymagań stawianych aplikacji. Jej szczegółowa znajomość jest wymagana w celu opracowania testów pokrywających wszystkie funkcje, które program powinien realizować. W związku z tym testy takie określane są jako funkcjonalne lub behawioralne. Duża część istniejących testów systemów informatycznych zaliczana jest do strategii czarnej skrzynki. Najczęściej spotykane pośród nich to testy funkcjonalne, testy wydajnościowe, testy obciążeniowe oraz testy systemowe. Należy jeszcze podkreślić, że zrealizowanie testów niefunkcjonalnych (np. wydajnościowych) możliwe jest jedynie przy zastosowaniu strategii czarnej skrzynki. 11

1.2. Wybrane typy testów W poprzedniej sekcji opisane zostały dwie strategie tworzenia testów systemów informatycznych. W oparciu o nie możliwy jest do zrealizowania szereg testów, które można pogrupować w cztery poniższe typy [2] : testy jednostkowe, testy integracyjne, testy funkcjonalne, testy niefunkcjonalne. Niestety nie wszystkie z powyższych testów mogą być zautomatyzowane. Na przykład do realizacji integracyjnych testów systemowych konieczna jest współpraca kilku systemów, za które odpowiedzialne mogą być odrębne organizacje. W kontekście niniejszej pracy ważne jest aby w sposób automatyczny sprawdzać funkcjonalności dodawane do realizowanego systemu i wykrywać powstałe przy tym błędy. Do tego celu najczęściej stosuje się automatyzację testów opisanych w kolejnych podrozdziałach. 1.2.1. Testy jednostkowe Testy jednostkowe (ang. unit tests) są realizacją strategii testowania metodą białej skrzynki. Są one tworzone w pierwszej kolejności i stanowią zazwyczaj największą pod względem liczebności, część testów budowanego oprogramowania. Oprócz wykrywania błędów, testy jednostkowe spełniają jeszcze dwie dodatkowe funkcje pozwalające zwiększyć jakość budowanego systemu oraz ułatwiające dalszy jego rozwój sprawdzając czy nie złamano wymagań. Testy takie dostarczają też swoistej dokumentacji i przykładów użycia poszczególnych jednostek kodu. Testy jednostkowe stosowane są we wszystkich istniejących językach programowania. Z czasem wykształciły się dedykowane biblioteki ułatwiające tworzenie i wykonywanie takich testów. Z powodu podobieństwa tych bibliotek zostały one określone mianem rodziny xunit. Przykładem takiej biblioteki dla języka Java jest JUnit zaprezentowany w Rozdziale 2. 12

1.2.1.1. Wykrywanie błędów Jak sama nazwa mówi testy jednostkowe skoncentrowane są na weryfikacji poprawnego działania poszczególnych jednostek kodu programu. Jako jednostkę rozumiemy tu najmniejszą testowalną [3] część aplikacji. W programowaniu proceduralnym jest to procedura lub funkcja, natomiast w językach obiektowych metoda klasy. Zgodnie z najlepszymi praktykami tworzenia testów jednostkowych dobry test to taki, który wykazuje następujące cechy: posiada wysokie prawdopodobieństwo wykrycia błędu, jego wykonanie jest szybkie, koncentruje się on jedynie na testowanej jednostce, elementy nie należące do danej jednostki są odizolowane. Właśnie w kontekście izolacji z testami jednostkowymi nierozerwalnie związane jest pojęcie obiektów zastępczych (ang. mock objects [3] ). Izolacja komponentów do testów jednostkowych jest trudna do osiągnięcia, ponieważ poszczególne jednostki kodu programu ze sobą współpracują. Często zdarza się, iż klasy współpracujące z testowaną istotnie wpływają na pewne elementy środowiska systemu. W takim wypadku autorzy testów rozszerzają kod testu również na zależności testowanej klasy, co powoduje że test z jednostkowego zamienia się w integracyjny lub funkcjonalny. Przykładem takiej sytuacji może być testowanie komponentu realizującego komunikację z bazą danych. Programista tworzący test zawiera w nim również komendy języka SQL wprowadzające dane do tabel w bazie, co w rzeczywistości stanowi testowanie interakcji z systemem zewnętrznym. W programowaniu obiektowym, realizację odseparowania klas na potrzeby testów umożliwia programowanie przez interfejsy oraz użycie obiektów zastępczych. Obiekty te imitują klasy wykorzystywane przez testowany komponent. Udostępniając identyczny interfejs, którego implementacja jest zazwyczaj pusta. W przytoczonym powyżej przykładzie zastąpiony mógłby zostać sterownik bazy danych, przez obiekt zawierający metody do wykonywania zapytań, jednak nie komunikujący się z rzeczywistą bazą. Realizację testów jednostkowych z użyciem obiektów zastępczych przedstawiono w rozdziale 4 w sekcji Testy jednostkowe. 13

1.2.1.2. Łamanie wymagań Dzięki testom jednostkowym w szybki sposób można określić czy wprowadzona w kodzie zmiana nie łamie wymagań stawianych poszczególnym komponentom. Jeżeli dla danego komponentu istnieje zestaw testów jednostkowych, mających za zadanie sprawdzenie czy spełnia on stawiane wymagania, to po wprowadzeniu do programu zmian łamiących te wymagania testy te przestaną się poprawnie wykonywać. W przypadku gdy testy wykonywały się poprawnie, a po wprowadzeniu zmian sygnalizują błędy, stanowią wtedy sposób wykrywania regresji w kodzie źródłowym i to ta właściwość jest najcenniejsza podczas automatycznego wykonywania testów w celach ciągłej integracji. Jednak uzyskanie takiego efektu wymaga odpowiednio dużego pokrycia testami kodu źródłowego aplikacji. 1.2.1.3. Dokumentacja Testy jednostkowe poszczególnych komponentów stanowią doskonałą dokumentację. Programiści, na podstawie testu jednostkowego danej klasy są w stanie uzyskać wiedzę na temat jej funkcjonalności. Często też zdarza się, że zmiany w kolejnych wersjach systemu nie są uwzględniane w formalnej dokumentacji. Z kolei testy jednostkowe same wymuszają konieczność aktualizacji, ponieważ w innym wypadku zgłaszają błędy. 1.2.2. Testy integracyjne Testy jednostkowe pozwalają zweryfikować zachowanie pojedynczych komponentów systemu. Jednak nawet jeżeli pracują one poprawnie w odosobnieniu, nie daje to pewności, że będą się poprawnie zachowywać współpracując ze sobą. Dzieje się tak dlatego, że do wykonywania stawianych im zadań, poszczególne moduły systemu 1 wykorzystują funkcjonalności dostarczane im przez inne moduły. Dlatego wymagane jest również wykonanie testów integracyjnych dla współpracujących ze sobą komponentów. Testy integracyjne są naturalnym rozszerzeniem testów jednostkowych i zazwyczaj wykonywane są przy użyciu tych samych narzędzi i bibliotek. Tym, co odróżnia je od testów jednostkowych jest zupełnie odmienne podejście do testowanych komponentów. Podczas testów integracyjnych najważniejsze jest zweryfikowanie czy współpraca pomiędzy poszczególnymi klasami i systemami zewnętrznymi przebiega w sposób 14

pożądany. Testy integracyjne są realizacją strategii testowania metodą czarnej skrzynki. Testy integracyjne mogą być realizowane na wiele sposobów, jednak najczęściej wykorzystywane są opisane poniżej trzy modele [1] : z góry na dół, z dołu do góry i hybrydowy. Przykłady realizacji testów integracyjnych dla systemu opartego o platformę Java Enterprise Edition zaprezentowano w Rozdziale 3. 1.2.2.1. Model z góry na dół W podejściu z góry na dół najpierw integrowane są moduły najwyższego poziomu a później wykorzystywane przez nie moduły zależne. Pozwala to w pierwszej kolejności testować logikę procesów aplikacji biznesowych oraz przepływ danych pomiędzy nimi. Niestety taki model wymaga intensywnego wykorzystania obiektów zastępczych ponieważ niektóre komponenty mogą nie być w danym momencie zaimplementowane. Występuje tu też ryzyko związane z relatywnie późnym integrowaniem elementów niskiego poziomu. Mimo, iż proces biznesowy wydaje się być obsługiwany poprawnie to zastąpienie obiektu zaślepki jednego z modułów niskiego poziomu może stać się niemożliwe do zaimplementowania, co z kolei wymusza zmianę w już utworzonych modułach. Zobrazować to można przykładem aplikacji korzystającej z bazy danych. Po zaimplementowaniu modułów wprowadzania danych może się okazać, iż niemożliwe jest zrealizowanie zapytań SQL w sposób jaki zakładano i koniecznym jest wprowadzania zmian w już zaimplementowanych modułach. Gdy opisana sytuacja zaistnieje pod koniec projektu zasoby potrzebne do realizacji zmian mogą już nie być dostępne. 1.2.2.2. Model z dołu do góry Podejście z dołu do góry wymusza testowanie i integrowanie elementów najniższego poziomu, to jest klas i ich metod, a następnie grupowanie przetestowanych elementów w moduły coraz to wyższego poziomu. Dzięki temu, podczas testów integracyjnych, zależności komponentów są już zaimplementowane i konieczność wykorzystywania obiektów zastępczych jest niewielka. Niestety, analogicznie do podejścia z góry na dół, pojawia się tu ryzyko związane z późnym testowaniem procesów wysokiego poziomu. Na przykład po dojściu do najwyższego poziomu może się okazać, iż współpraca poszczególnych modułów nie może przebiegać w sposób jaki zakładano i wymagane jest ponowne zaimplementowanie kilku z nich. Jeżeli taka sytuacja wystąpi w końcowych fazach projektu, może już nie być czasu na zmiany. 15

1.2.2.3. Model hybrydowy Model hybrydowy stara się wykorzystać zalety obu powyższych podejść i zminimalizować ich wady. Najpierw integrowane są moduły stanowiące interfejsy poszczególnych funkcjonalności przy użyciu modelu z góry na dół, a następnie funkcje wyjściowe tych modułów integrowane są w sposób z dołu do góry. W przykładowym systemie informatycznym mogłoby to odbywać się tak, iż najpierw zintegrowane zostałyby moduły odpowiedzialne za obsługę interfejsu użytkownika dla danej funkcjonalności, a następnie niskopoziomowe moduły realizujące komunikację z bazami danych i systemami zewnętrznymi 1.2.3. Testy funkcjonalne Zestawy testów funkcjonalnych są odzwierciedleniem wymagań funkcjonalnych stawianych systemowi i tworzone są przy współpracy klientów biznesowych, analityków systemu, testerów i programistów. Język testów funkcjonalnych odzwierciedla domenę systemu. Operuje się w nim pojęciami takimi, jak na przykład konto w systemie bankowym czy przesyłka w systemie kurierskim, które powinny być definiowane przez przedstawicieli klienta, nazywanych potocznie ekspertami domeny [1]. Eksperci domeny posiadają niezbędną wiedzę na temat procesów biznesowych, które system ma realizować. Testy funkcjonalne to testy metodą czarnej skrzynki sterowane takimi danymi, które byłyby wprowadzane przez użytkowników końcowych aplikacji przez dedykowane im interfejsy. Stanowią narzędzie pozwalające przeprowadzać walidację tworzonego oprogramowania czyli sprawdzać: Czy budowany jest ten system co trzeba. Czy zrealizowane zostały wszystkie wymagania klienta. Czy system spełnia oczekiwania klienta. Tym samym, stanowią doskonały sposób na stwierdzenie kiedy dany przypadek użycia, a nawet cały system został w pełni oprogramowany wtedy kiedy wszystkie zdefiniowane dla niego testy funkcjonalne dają wynik pozytywny. Poprawne przejście zestawu testów funkcjonalnych stanowi zazwyczaj kryterium akceptacji odbioru systemu przez zamawiającego, dlatego testy takie często nazywane są testami akceptacyjnymi. 16

Testy te wykonywane są w środowisku odzwierciedlającym docelowe środowisko systemu z uwzględnieniem wszystkich wymagań aplikacji, takich jak dostępność zasobów softwarowych (np. zainstalowana przeglądarka internetowa) lub też sprzętowych (karta dźwiękowa, połączenie internetowe). Testy takie przeznaczone są do wykonywania przez testerów i ich realizacja w sposób umożliwiający automatyczne wykonywanie w procesie ciągłej integracji nie może być pominięta. Jednak dzięki zastosowaniu dostępnych narzędzi możliwe jest wykonanie testów funkcjonalnych w sposób automatyczny.. 1.2.4. Test niefunkcjonalne Istnieje szereg oczekiwań stawianych przez klienta w stosunku do tworzonego oprogramowania, których nie da się w sposób jednoznaczny powiązać z dostarczaną funkcjonalnością. Przykładem takiego wymagania jest zachowywanie odpowiednich czasów przetwarzania danych wprowadzonych przez użytkowników i czasów odpowiedzi na poszczególne zapytania co możemy nazwać wydajnością systemu. Przetestowanie wymagań niefunkcjonalnych jest możliwe jedynie poprzez zastosowanie strategii czarnej skrzynki. W kolejnych podrozdziałach przedstawiono najczęściej występujące testy niefunkcjonalne. W Rozdziale 4 przedstawiono sposób realizacji testów wydajnościowych usługi sieciowej przy użyciu narzędzia SoapUI. 1.2.4.1. Wydajność Sformatowane: Punktory i numeracja Testy wydajnościowe pozwalają na wykrycie tych spośród komponentów systemu, które nie zachowują odpowiednich metryk wydajnościowych i wymagają optymalizacji. Służą również do identyfikacji potencjalnych wąskich gardeł, które obniżają sprawność systemu w przypadku zwiększonego obciążenia (np. komunikacja z systemami zewnętrznymi lub nadmierne zużycie pamięci). 1.2.4.2. Obciążenie Sformatowane: Punktory i numeracja Testowanie obciążeniowe pozwala zweryfikować, czy nałożone wymagania wydajnościowe są spełniane w przypadku, gdy system używany jest równolegle przez zdefiniowaną liczbę użytkowników. 17

1.2.4.3. Wytrzymałość Sformatowane: Punktory i numeracja Testy wytrzymałościowe, powszechnie stosowane do wyznaczenia maksymalnej liczby użytkowników, których system jest w stanie jednocześnie obsłużyć. Jeżeli liczba ta jest mniejsza od określonej w wymaganiach, zachowanie systemu uznawane jest za błędne. W kryterium wytrzymałości zawiera się też weryfikacja spełnienia wymagań wydajnościowych w przypadku chwilowego, znacznie zwiększonego obciążenia systemu. Jest to szczególnie istotne podczas testowania systemów dostępnych publicznie, np. portali internetowych w których obciążenie może wzrosnąć drastycznie po opublikowaniu jakiejś wiadomości. 1.2.5. Testowalność oprogramowania W pracy przedstawione już zostały typy możliwych do wykonania testów. Jednak, aby ich uruchomienie przyniosło jakiekolwiek efekty, system, który jest sprawdzany musi być testowalny. Do głównych cech testowalnego oprogramowania należą: operatywność, obserwowalność, sterowność, dekompozycja, prostota, stabilność, zrozumiałość [1]. 1.2.5.1. Operatywność System ma niewiele błędów. Błędy nie blokują wykonania testów. 1.2.5.2. Obserwowalność Stany lub zmienne systemu są widoczne lub sprawdzalne podczas wykonania. Wszystkie czynniki wpływające na generowane wyniki są widoczne. Niepoprawne dane wyjściowe są w łatwy sposób identyfikowane. Błędy wewnętrzne są raportowane w sposób automatyczny. 18

1.2.5.3. Sterowność Im bardziej można sterować systemem, tym więcej testów może być przeprowadzone w sposób automatyczny. Wszystkie możliwe dane wyjściowe generowane są przez kombinacje danych wejściowych. Całość kodu systemu jest wykonana przez wprowadzanie kombinacji danych wejściowych. Testy mogą być dowolnie określane, automatyzowane i powtarzane. 1.2.5.4. Dekompozycja Kontrolując zakres testu można szybko izolować problemy. System zbudowany jest z niezależnych modułów. Poszczególne moduły mogą być testowane w sposób niezależny. 1.2.5.5. Prostota Prostota funkcjonalności system składa się z minimum funkcjonalności spełniających wymagania klienta. Prostota strukturalna architektura systemu jest modularna. Prostota kodu kod spełnia określone standardy ułatwiające inspekcję. 1.2.5.6. Stabilność Zmiany wprowadzane do systemu są kontrolowane. Wprowadzone zmiany nie łamią dotychczasowych testów. 1.2.5.7. Zrozumiałość Zależności pomiędzy komponentami systemu są dobrze zrozumiałe. Zmiany w projekcie są jasno komunikowane. Dokumentacja techniczna systemu jest dostępna. 19

1.3. Ciągła integracja Termin ciągła integracja w odniesieniu do systemów informatycznych wszedł do powszechnego użycia po opublikowaniu przez Martina Fowlera artykułu Continous Integration [4]. Pomimo mylnych opinii proces ciągłej integracji nie zastępuje testowania systemu informatycznego, a jest jedynie jego dopełnieniem pozwalającym znacznie zwiększyć efektywność testów poprzez ich częste, w pełni automatyczne wykonywanie. W swoim artykule Fowler przedstawia etap integracji dużego projektu informatycznego jako długi i nieprzewidywalny proces. Proces tym dłuższy i tym bardziej nieprzewidywalny, im dalej w czasie odwlekana jest integracja i testowanie projektu. Na bazie własnych doświadczeń oraz doświadczeń swoich współpracowników z firmy Thought Works, zdefiniował on szereg praktyk, którymi powinny się kierować projekty informatyczne w celu uproszczenia procesu integracji systemu i możliwości jego wykonania na żądanie w dowolnej chwili. Poniżej krótko scharakteryzowano najważniejsze z nich: 1.3.1. Utrzymywanie pojedynczego repozytorium kodu Według Fowlera, narzędzia kontroli wersji są integralną częścią każdego, nawet najmniejszego projektu. Mnogość plików i częstotliwość zmian jakie w nich zachodzą wymusza stosowanie repozytorium kodu źródłowego w celu monitorowania modyfikacji jakim poddawana jest aplikacja i w razie konieczności przywracania poprawnie działających wersji. Oprócz utrzymywania historii poszczególnych plików repozytorium ma za zadanie ułatwienie nowym programistom rozpoczęcie pracy z projektem. Jednak aby to osiągnąć, systemy kontroli wersji powinny zawierać nie tylko kod źródłowy aplikacji, ale również wszystkie skrypty kompilacyjne, uruchomieniowe, a także biblioteki używane w projekcie. 1.3.2. Automatyzacja budowy Każdy system powinien być wyposażony w skrypt budujący poszczególne komponenty i składający je w całość (w postaci bibliotek lub plików wykonywalnych). Każdorazowe, ręczne wykonywanie procesu budowy poszczególnych elementów programu wprowadza niepotrzebny narzut czasowy oraz umożliwia popełnienie błędów. 20