Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!

Testowanie modułów z użyciem frameworka SimpleTest

Wprowadzenie do testowania modułów

Powiedzieliśmy już trochę o programowaniu ekstremalnym, ale jeszcze niewiele wiemy na temat programowania sterowanego testami. Zanim wytłumaczymy to zagadnienie zastanówmy się, jak wygląda debugowanie typowej aplikacji napisanej w PHP. Piszemy więc kod, następnie uruchamiamy go i jeśli coś poszło nie tak, dopisujemy szereg instrukcji echo i var_dump(), które wyświetlą nam na ekranie aktualny stan zmiennych, tablic, czy obiektów. Gdy znajdziemy błąd, piszemy dalej. Uruchamiamy kod i jeśli mamy szczęście, to wszystko zadziała. Często jednak znowu widzimy tylko Warning lub Fatal Error. Co robimy? Dopisujemy kilka kolejnych instrukcji echo i var_dump(). I tak w kółko. Gdy wreszcie skrypt zacznie działać stabilnie, możemy spokojnie wyczyścić kod (do czasu, gdy znowu będzie trzeba wprowadzić zmiany).

Opisana technika debugowania jest nudna, monotonna i czasem bardzo irytująca. Mimo to, jest ona stosowana przez znaczną większość programistów PHP. Istnieją jednak sposoby bardziej efektywne. Jednym z nich jest testowanie modułów (ang. Unit testing). Polega ono na tym, że dla każdej klasy piszemy klasę testującą, która dokładnie sprawdzi jej zachowania w różnych sytuacjach i reakcje na różne dane. Choć TDD kojarzone jest przede wszystkim z programowaniem obiektowym, nic nie stoi na przeszkodzie, abyśmy stosowali testowanie modułów również dla kodu proceduralnego. Większość dostępnych w literaturze, czy w Sieci przykładów opiera się jednak na OOP, więc my także będziemy się tego trzymać.

Dostępne narzędzia

Aby w pełni korzystać z zalet testowania modułów, potrzebujemy odpowiednich narzędzi. Złym pomysłem jest pisanie własnych rozwiązań od podstaw, ponieważ każdy chyba obiektowy język programowania posiada świetne frameworki pomocne w testowaniu modułów. Znaczna większość z nich jest wzorowana na znakomitym, stworzonym dla Javy JUnit, którego autorami są Erich Gamma i Kent Beck. Programiści języka PHP również mają do dyspozycji kilka podobnych narzędzi, z których liczą się tylko dwa: napisany przez Sebastiana Bergmanna PHPUnit, który należy do repozytorium PEAR oraz SimpleTest autorstwa Marcusa Bakera.

Ten drugi oferuje nieporównywalnie większą funkcjonalność, dlatego to właśnie nim posłużymy się, aby zaprezentować technikę TDD. Jeśli posiadamy już zestaw testów przygotowanych wcześniej specjalnie dla PHPUnit, jest duża szansa, że uruchomimy je także za pomocą SimpleTest.

Instalacja SimpleTest

Instalacja frameworka jest bardzo prosta i ogranicza się w zasadzie do pobrania archiwów z Internetu i rozpakowania ich we wskazanym miejscu na dysku. Najnowszą wersję możemy zawsze pobrać ze strony projektu na SourceForge.net: https://sourceforge.net/projects/simpletest/.

Listing 1. Pierwszy test

<?php
// plik validator_test.php
require_once('./simpletest/unit_tester.php');
require_once('./simpletest/reporter.php');
require_once('../validator.php');
class ValidatorTestCase extends UnitTestCase {
    private $validator = null;
    public function __construct() {
        parent:
        :__construct();
        $this->validator = new Validator();
    }
    public function testLogin() {
        $this->assertTrue($this->validator->isValidLogin('testlogin'));
        $this->assertTrue($this->validator->isValidLogin('testlogin123'));
        $this->assertTrue($this->validator->isValidLogin('1234'), 'Login może składać sie również z samych cyfr!');
        $this->assertFalse($this->validator->isValidLogin('test^#login$&@'), 'Login może składać się tylko z liter lub cyfr!');
    }
}
$test = new ValidatorTestCase();
$test->run(new HtmlReporter());
?>
<?php
//plik validator.php
class Validator {
    public function isValidLogin($login) {
    }
}
?>
Kiedy zacząć testy?

To oczywiste, że znacznie łatwiej znaleźć błąd w programie składającym się ze 100 linii, niż w tym, który ma ich wiele tysięcy. Dlatego testowanie powinniśmy rozpocząć jak najwcześniej - najlepiej jeszcze przed napisaniem jakiegokolwiek kodu naszego programu. Tylko co mamy testować, jeśli jeszcze niczego nie napisaliśmy? Zaraz wszystko się wyjaśni. Zacznijmy od przykładu.

Pierwsze testy

Przypuśćmy, że tworzymy system rejestracji użytkowników i potrzebujemy mechanizmu do sprawdzania poprawności przesyłanych danych. Stworzymy więc prostą klasę, która nam to umożliwi. Dla zachowania maksymalnej prostoty skupimy się tylko na metodzie sprawdzającej poprawność loginu. Najpierw napiszmy test, później interfejs klasy, co ukazaliśmy na Listingu 1. Załóżmy, że prawidłowy login może składać się tylko i wyłącznie z liter lub cyfr.

Przeanalizujmy teraz kod z Listingu 1. Na samym początku włączamy trzy pliki: pierwszy z nich zawiera definicję klasy UnitTestCase, a drugi klasę HtmlReporter, której zadaniem jest wyświetlanie komunikatów na ekranie. Trzecim plikiem jest klasa Validator, którą będziemy testować.

Każdy przypadek testowy jest pochodną klasy UnitTestCase. Metody, których nazwy rozpoczynają się od słowa test, framework traktuje jako właściwe testy i uruchamia automatycznie.

Wewnątrz metody testLogin() widzimy wywołanie metody assertTrue(). Pozwala ono upewnić się, że dane wyrażenie jest prawdziwe. W naszym przypadku tym wyrażeniem jest $this->validator-> isValidLogin(). Jeśli isValidLogin() zwróci false, to znaczy, że test się nie powiódł. Drugi parametr jest opcjonalny. Jest to komunikat, który zostanie wypisany na ekran w przypadku niepowodzenia testu. Jeśli nie podamy tego parametru, SimpleTest wyświetli standardową informację o błędzie. Klasa UnitTestCase zawiera więcej funkcji typu assert. Ich listę wraz z opisem zamieściliśmy w Tabeli 1, a przykłady zastosowań na Listingu 2.

Spróbujmy zatem uruchomić nasz test. Robimy to wywołując skrypt validator_ test.php w przeglądarce internetowej. Wynik był do przewidzenia: nie zaimplementowaliśmy jeszcze klasy Validator, więc testy się nie udały (Rysunek 1). Ale chwileczkę! Na ekranie widać, że tylko trzy z czterech testów są nieudane.

$this->assertIdentical($this->validator->isValidLogin('test^#login$&@'), false);

Listing 2. Przykłady zastosowań funkcji typu assert...()

<?php
$x = new Validator();
$this->assertIsA($x, 'Validator');
$x = 125;
$this->assertEqual($x, 125);
$x = 10;
$y =& $y;
$this->assertReference($x, $y);
$this->assertNull($not_initialized_variable);
$this->assertWantedPattern('/^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$/','email@mysite.com', 'Nieprawidłowy adres email');
trigger_error('Błąd krytyczny');
$this->assertError('Błąd krytyczny');
?>

Listing 3. Pierwsza działająca wersja naszej klasy

<?php
class Validator {
    /* ... */
    public function isValidLogin($login){
        if (!preg_match('/^[a-zA-z0-9]+$/', $login)) {
            return false;
        }
        return true;
    }
}
?>

Listing 4. Test rozszerzony o dodatkowe warunki

<?php
public function testLogin() {
    /* ... */
    $this->assertFalse($this->validator->isValidLogin(''));
    $this->assertFalse($this->validator->isValidLogin('abc'),
    'Login musi mie&#263; przynajmniej 4 znaki');
    $this->assertFalse($this->validator->isValidLogin('tenLoginJestZdecydowanieZaDlugi'), 'Login nie może być dłuższy niż 15 znaków');
}
?>

Rysunek 1. Nieudany test

Czas na implementację

Główna zasada programowania sterowanego testami mówi, żeby nie tworzyć żadnego kodu do czasu, gdy test aplikacji okaże się nieudany. Ponieważ nasz test się nie powiódł, więc spróbujmy napisać kod, który przejdzie go pomyślnie (Listing 3).

Teraz metoda isValidLogin() sprawdza, czy login i hasło zawierają tylko dozwolone znaki (litery i cyfry). Spróbujmy uruchomić test. Tym razem kod przechodzi go pomyślnie (Rysunek 2) i - co najważniejsze - działa. Niestety, nie możemy być w pełni zadowoleni z funkcjonalności. Nie przewidzieliśmy bowiem, że użytkownik może w ogóle nie wypełnić pól formularza albo wpisać za długi lub za krótki login. Dodajmy kilka warunków testowych, by przekonać się, jak Validator zachowa się w tej sytuacji.

Spójrzmy teraz na Listing 4. Dodaliśmy kilka warunków - najpierw spróbujemy upewnić się, że pusty login zostanie potraktowany jako nieprawidłowy, a następnie sprawdzamy, jak Validator zareaguje na za krótki, a potem na zdecydowanie za długi login. Uruchamiamy test. Tak, jak myśleliśmy, klasa wymaga dopracowania. Czas więc na mały refaktoring (patrz Ramka Refaktoring). Poprawimy wyrażenie regularne w funkcji isValidLogin(). Powinno ono mieć następującą postać:

if (!preg_match('/^[a-zA-z0-9]{4,15}$/)) {

Funkcja będzie teraz wzorcowo rozpoznawała nieprawidłowe dane. Uruchamiamy test - udało się. Validator przechodzi wszystkie testy pomyślnie i jest gotowy do użytku.

Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (1)
Usrapwnienia wizualne
Niedziela 15 Styczeń 2006 12:35:06 pm - aztech <scrabblewroclaw_at_op.pl>

Proponowałbym podlinkowanie wszystkich odnośników pojawiających (część jest, część natomiast nie - konrketnie w części artukułu: SimpleTest + Eclipse). Proponowałbym także wprowadzić podlinkowania do listingów (myślę, że to byłaby dobra praktyka dla wszystkich artykułów - szalenie ułatwia czytanie), ale tak aby przenosiły w miejsce listingu a nie tylko na stronę, gdzie znajduje się listing (a href="strona.html#name").
Można by w sumie też zrobić highlighting najważniejszych terminów, nazw klas, nazw funkcji (ale niekoniecznie, jeśli miałoby to zaciemnić artykuł).
P.S. Artykuł ciekawy. Z racji, że nigdy nie używałem SimpleTest, a zamierzam się nim pobawić po przeczytaniu artykułu, postaram się napisać wkrótce opinię, na ile sam artykuł pomaga w bezproblemowe (problemowe :D) wejście w SimpleTest

Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2022 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System