Pierwsze kroki z Nette Tester

Nawet dobrzy programiści popełniają błędy. Różnica między dobrym a złym programistą polega na tym, że ten dobry popełni go tylko raz, a następnym razem wykryje go za pomocą zautomatyzowanych testów.

  • “Kto nie testuje, jest skazany na powtarzanie swoich błędów.” (przysłowie)
  • “Jak tylko pozbędziemy się jednego błędu, pojawi się kolejny.” (prawo Murphy'ego)
  • “Kiedykolwiek masz ochotę wypisać sobie na ekran zmienną, napisz raczej test.” (Martin Fowler)

Czy kiedykolwiek napisałeś w PHP podobny kod?

$obj = new MyClass;
$result = $obj->process($input);

var_dump($result);

Czyli wypisałeś sobie wynik wywołania funkcji tylko po to, aby okiem zweryfikować, czy zwraca to, co powinna? Na pewno robisz to wiele razy dziennie. Ręka na serce: w przypadku, gdy wszystko działa poprawnie, usuwasz ten kod? Oczekujesz, że klasa w przyszłości się nie zepsuje? Prawa Murphy'ego gwarantują coś przeciwnego :-)

W zasadzie napisałeś test. Wystarczy go tylko nieznacznie zmodyfikować, aby nie wymagał kontroli wzrokowej, ale aby sprawdził się sam. A jeśli testu nie usuniesz, możesz go uruchomić kiedykolwiek w przyszłości i zweryfikować, czy wszystko nadal działa, jak powinno. Z czasem stworzysz dużą liczbę takich testów, więc przydałoby się je uruchamiać automatycznie.

A z tym wszystkim pomoże Ci właśnie Nette Tester.

Co wyróżnia Testera?

Pisanie testów dla Nette Tester jest unikalne w tym, że każdy test jest zwykłym skryptem PHP, który można uruchomić samodzielnie.

Czyli gdy piszesz test, możesz go po prostu uruchamiać i sprawdzać, czy na przykład nie ma w nim błędu programistycznego. Czy działa poprawnie. Jeśli nie, możesz go łatwo krokować w swoim IDE i szukać błędu. Możesz go nawet otworzyć w przeglądarce.

A przede wszystkim – tym, że go uruchomisz, wykonasz test. Natychmiast dowiesz się, czy przeszedł, czy zawiódł. Jak? Pokażmy to sobie. Napiszemy trywialny test pracy z tablicą PHP i zapiszemy do pliku ArrayTest.php:

<?php
use Tester\Assert;

require __DIR__ . '/vendor/autoload.php';  # wczytanie autoloadera Composera
Tester\Environment::setup();               # inicjalizacja Nette Tester

$stack = [];
Assert::same(0, count($stack));   # oczekujemy, że count() zwróci zero

$stack[] = 'foo';
Assert::same(1, count($stack));   # oczekujemy, że count() zwróci jedynkę
Assert::contains('foo', $stack);  # zweryfikujemy, czy $stack zawiera element 'foo'

Jak widzisz, tzw. metody asercji jak Assert::same() służą do potwierdzenia, że rzeczywista wartość odpowiada oczekiwanej wartości.

Test mamy napisany i możemy go uruchomić z wiersza poleceń. Pierwsze uruchomienie ujawni ewentualne błędy składniowe, a jeśli nigdzie nie zrobiłeś literówki, wypisze się:

$ php ArrayTest.php

OK

Spróbuj w teście zmienić twierdzenie na nieprawdziwe Assert::contains('XXX', $stack); i obserwuj, co się stanie przy uruchomieniu:

$ php ArrayTest.php

Failed: ['foo'] should contain 'XXX'

in ArrayTest.php(17) Assert::contains('XXX', $stack);

FAILURE

Dalej o pisaniu kontynuujemy w rozdziale Pisanie testów.

Instalacja i wymagania

Minimalna wersja PHP wymagana przez Testera to 7.1 (szczegółowiej w tabeli Obsługiwane wersje PHP). Preferowany sposób instalacji to za pomocą Composer:

composer require --dev nette/tester

Spróbuj z wiersza poleceń uruchomić Nette Tester (bez parametrów wypisze tylko pomoc):

vendor/bin/tester

Uruchamianie testów

Jak aplikacja rośnie, liczba testów rośnie wraz z nią. Nie byłoby praktyczne uruchamiać testów pojedynczo. Dlatego Tester dysponuje masowym narzędziem do uruchamiania testów, które wywołujemy z wiersza poleceń. Jako parametr podajemy katalog, w którym znajdują się testy. Kropka oznacza bieżący katalog.

vendor/bin/tester .

Narzędzie do uruchamiania testów przeszuka podany katalog i wszystkie podkatalogi i wyszuka testy, czyli pliki *.phpt i *Test.php. Znajdzie więc również nasz test ArrayTest.php, ponieważ pasuje do maski.

Następnie rozpocznie testowanie. Każdy test uruchomi jako nowy proces PHP, więc przebiega całkowicie odizolowany od pozostałych. Uruchamia je równolegle w wielu wątkach, dzięki czemu jest niezwykle szybki. A jako pierwsze uruchamia testy, które przy poprzednim uruchomieniu zawiodły, więc natychmiast dowiesz się, czy udało Ci się naprawić błąd.

Podczas wykonywania testów Tester wypisuje na bieżąco wyniki na terminal jako znaki:

  • . – test przeszedł
  • s – test został pominięty (skipped)
  • F – test zawiódł (failed)

Wyjście może wyglądać tak:

 _____ ___  ___ _____ ___  ___
|_   _/ __)( __/_   _/ __)| _ )
  |_| \___ /___) |_| \___ |_|_\  v2.5.2

Uwaga: Nie użyto pliku php.ini.
PHP 8.3.2 (cli) | php -n | 8 wątków

........s................F.........

-- FAILED: greeting.phpt
   Failed: 'Hello John' should be
       ... 'Hello Peter'

   in greeting.phpt(19) Assert::same('Hello Peter', $o->say('John'));

FAILURES! (35 tests, 1 failures, 1 skipped, 1.7 seconds)

Uruchomiono 35 testów, jeden zawiódł, jeden został pominięty.

Dalej kontynuujemy w rozdziale Uruchamianie testów.

Tryb Watch

Refaktoryzujesz kod? A może nawet rozwijasz według metodyki TDD (Test Driven Development)? W takim razie spodoba Ci się tryb watch. Tester w nim śledzi kody źródłowe i przy zmianie sam się uruchamia.

Podczas rozwoju masz więc w rogu monitora terminal, gdzie świeci na Ciebie zielony pasek stanu, a gdy nagle zmieni się na czerwony, wiesz, że właśnie czegoś nie zrobiłeś do końca dobrze. To właściwie świetna gra, w której programujesz i starasz się utrzymać kolor.

Tryb watch uruchamia się parametrem –watch.

Raporty CodeCoverage

Tester potrafi generować raporty z przeglądem, ile kodu źródłowego pokrywają testy. Raport może być albo w czytelnym dla człowieka formacie HTML, albo Clover XML do dalszego przetwarzania maszynowego.

Zobacz przykładowy raport HTML z pokryciem kodu.

Obsługiwane wersje PHP

wersja kompatybilna z PHP
Tester 2.5 PHP 8.0 – 8.3
Tester 2.4 PHP 7.2 – 8.2
Tester 2.3 PHP 7.1 – 8.0
Tester 2.1 – 2.2 PHP 7.1 – 7.3
Tester 2.0 PHP 5.6 – 7.3
Tester 1.7 PHP 5.3 – 7.3 + HHVM 3.3+
Tester 1.6 PHP 5.3 – 7.0 + HHVM
Tester 1.3 – 1.5 PHP 5.3 – 5.6 + HHVM
Tester 0.9 – 1.2 PHP 5.3 – 5.6

Dotyczy ostatniej wersji poprawki.

Tester do wersji 1.7 obsługiwał również HHVM 3.3.0 lub wyższy (przez tester -p hhvm). Wsparcie zostało zakończone od wersji Testera 2.0.