Testy z Codeception w CodeIgniter

Kiedyś wspominaliśmy już o tym czemu warto pisać testy dla swoich aplikacji. Dzisiaj chciałbym pokazać, jak można zintegrować CodeIgniter z Codeception. Czym jest Codeception? W uproszczeniu można powiedzieć, że to taki „kombajn” do testów – dzięki czemu możemy przeprowadzać różne rodzaje testów (jednostkowe, funkcjonalne i akceptacyjne). Mam nadzieję, że brzmi to zachęcająco.

Za co odpowiadają konkretne testy

Zanim zaczniemy, w kilku słowach wspomnę o rodzajach testów. Można oczywiście sięgnąć po definicje z wiki lub jakiejś książki, ale przedstawię tutaj w miarę proste przykłady, które moim zdaniem dosyć dobrze oddają przeznaczenie każdego z rodzaju testów.

Testy jednostkowe.
Testujemy konkretną metodę naszego kodu źródłowego. Dostarczamy potrzebne parametry wejściowe i sprawdzamy, czy wynik zgadza się z tym czego oczekujemy.

Testy funkcjonalne.
Testujemy funkcję w naszej aplikacji. Np. rejestrację. Dostarczamy dane wejściowe, tutaj będzie to email i hasło, i nie interesuje nas już co się dzieje „pod spodem” (nie ważne z ilu różnych metod po drodze korzystamy), chcemy ustalić, czy funkcja rejestracji działa. Możemy więc sprawdzić jaki jest wynik wysłania formularza i czy np. został wysłany email z potwierdzeniem rejestracji albo czy zostaliśmy automatycznie zalogowani (sprawdzamy stan sesji).

Testy akceptacyjne.
Testujemy naszą aplikację od strony użytkownika. Innymi słowy sprawdzamy, czy użytkownicy są w stanie poprawnie korzystać z naszej strony. Podczas testów możemy więc polegać tylko na tym co widzi użytkownik. Możemy więc np. sprawdzić, czy wypełnienie opowiednich pól w formularzu i kliknięcie w konkretny przycisk pozwoli nam się zalogować.

 Po co mi Codeception?

PHPUnit jest najpopularniejszą metodą testowania aplikacji w PHP. Nic dziwnego, to świetne narzedzie. Jednak testy jednostkowe sprawdzają się najlepiej, kiedy chcemy przetestować konkretne zachowanie. Np. metodę modelu, metodę biblioteki, metodę API lub funkcję w helperze. Sprawa wygląda gorzej, kiedy chcemy testować „zwykłe” kontrolery w naszej aplikacji – czyli sprawdzić, czy tak naprawdę nasi użytkownicy są w stanie używać naszej aplikacji i nie napotkają na błędy. W takim przypadku PHPUnit nie jest najporęczniejszym narzędziem.

Przykładowo, chcemy przetestować taki scenariusz:
Wchodząc na stronę „login”, podaję nazwę użytkownika i hasło. Po kliknięciu w przycisk „Zaloguj” zostaję zalogowany na stronę.

Ciężko byłoby zrealizować taki scenariusz w PHPUnit. Na szczęście Codeception pozwala nam na tworzenie testów akceptacyjnych, które świetnie nadają się do sprawdzania tego rodzaju rzeczy. Co więcej, w Codeception możemy korzystać również z testów jednostkowych. Tym sposobem dzięki jednemu narzędziu, możemy przeprowadzać różne rodzaje testow.

Do dzieła

No dobrze, skoro już wiemy czemu warto się zainteresować tym narzędziem, możemy przystąpić do instalacji.

  • Na początek tworzymy nowy, czysty projekt z CI 3.0.4. Nie zapomnijmy o podstawowej konfiguracj, tak żebyśmy mogli używać strony.
  • Teraz pora pobrać Codeception. Możemy to zrobić na tej stronie. Zwróćmy uwagę na to, że zależnie od wersji PHP, której używamy, musimy ściągnąć różne wersje pliku. Ja użyję polecenia:
    wget http://codeception.com/codecept.phar. Pamiętajcie, że plik codeception.phar powinien się znaleźć w głównym katalogu naszego projektu CI.
  • Następny krok to inicjalizacja testów. Z poziomu konsoli wpisujemy: php codecept.phar bootstrap. Utworzony zostnie katalog „tests” oraz plik konfiguracyjny „codeception.yml”.
  • Pora przygotować specjalny plik, który pozwoli na uruchamianie testów dla CI. Znajdzie się tam klasa „CodeIgniterTestCase”, z której będziemy korzystać przy pisaniu testów jednostkowych.
  • Teraz musimy edytować plik „tests/_bootstrap.php” i dopisać dwie linijki:
  • Pozostało jeszcze zmodyfikować lekko jedną metodę klasy Router, abyśmy mogli uruchomić CodeIgniter w Codeception. Musimy więc tę klasę rozszerzyć:

Testy jednostkowe

  • Możemy teraz sprawdzić, czy jesteśmy w stanie uruchomić testy PHPUnit. Najpierw stwórzmy jakąś przykładową metodę w modelu (bardzo prostą).
  • Teraz pora na testy:

Skoro wszystko jest już gotowe, spróbujmy uruchomić testy jednostkowe. W tym celu należy wykonac komendę: php codecept.phar run unit. Jeśli wszystko zrobiliśmy dobrze, to testy powinny „przejść”. Tym sposobem uruchomiliśmy testy jednostkowe w Codeception.

Testy akceptacyjne

  • Teraz pora na testy akceptacyjne. Najpierw edytujemy plik konfiguracyjny „tests/acceptance.suite.yml”. Musimy podać aktualny adres URL, pod którym znajduje się nasz lokalny projekt CI.
  • Czas stworzyć jakiś przykładowy kontroler i widok, dla których napiszemy testy

  • Możemy teraz stworzyć nasze testy akceptacyjne.

Przykład jest bardzo prosty, ale pozwoli nam sprawdzić, czy możemy uruchomić testy. Wykonajmy więc komendę z konsoli: php codecept.phar run acceptance.
Kolejny raz – jeśli wszystko zrobiliśmy dobrze, to testy powinny wykonać się poprawnie. W ten sposób możemy uruchamiać zarówno testy jednostkowe, jak i akceptacyjne – świetnie.

Jeśli chodzi o testy akceptacyjne, to jest jeszcze kilka kwestii, o których chciałbym wspomnieć. Pierwszą ważną sprawą jest używanie modułu bazy danych przy tworzeniu testów. Codeception posiada specjalny moduł „Db”, który to umożliwia. Więcej szczegółów oraz informacji jak z niego korzystać znajdziemy pod tym linkiem.

Drugą sprawą jest sposób przeprowadzania testów akceptacyjnych. W tej chwili w pliku konfiguracyjnym (tests/acceptance.suite.yml), do uruchamiania testów używamy crawlera, który komunikuje się z naszą strona za pomocą cURL (ustawienie: PhpBrowser). Ma to niestety swoje minusy. Jednym z nich jest, to że nie możemy testować tych rzeczy, które są napisane w JavaScript. Innymi słowy, uruchamiając testy w ten sposob nie mamy takich samych doświadczeń jak użytkownik posługujący się zwykłą przeglądarką internetową. Na szczęście jest sposób na to, żeby to ominąć – z pomocą przyjdzie nam server Selenium.

Serwer Selenium

Aby uruchamiać testy akceptacyjne przy pomocy serwera Selenium wystarczą w zasadzie dwie rzeczy:

  1. Zmina ustawień w pliku konfiguracyjnym – zmiany są kosmetyczne, ale jednak kluczowe:

    Czemu ustawiamy przeglądarke na Firefox? Dla wygody. Serwer Selenium ma wbudowaną obsługe dla tej przeglądarki, jeśli chcemy użyć np. Chrome, to musimy pobrać dodatkowy moduł. Tak więc dla uproszczenia zostaniemy przy przeglądarce Firefox – będzie nam poprostu łatwiej.
  2. Instalacja serwera Selenium
    Pobieramy plik ze strony projektu (Selenium Standalone Server) i umieszczamy go w głównym katalogu naszego projektu CI. W naszym wypadku jest to wersja 2.52.0 serwera Selenium. Teraz musimy uruchomoć serwer – posłużymy się komendą: java -jar selenium-server-standalone-2.52.0.jar

Serwer wystartował. Jeśli odpalimy teraz testy php codecept.phar run acceptance, to powinna uruchomic się przeglądarka Firefox i przeprowadzić czynności, które mamy w testach, czyli: wejść na stronę „login”, wypełnić formularz i go wysłać. W konsoli zobaczymy końcowy wynik dla testów, tak jak poprzednio. Jeśli chcemy wyłączyć serwer Selenium, to wystarczy użyć skrótu Ctrl + Z.

Podsumowanie

Na koniec mała uwaga co do zmiennej środowiskowej ENVIRONMENT, którą posługujemy się w CodeIgniter.
Testy akceptacyjne są uruchamiane w środowisku, które mamy ustawione lokalnie, czyli w takim, w którym normalnie wchodzimy na stronę. Zazwyczaj będzie to środowisko „development”. Testy powinniśmy uruchamiać tylko w środowisku testowym, więc mamy dwa wyjścia. Powinniśmy zmieniać środowisko przed każdym uruchomieniem testow (bardzo niewygodne), lub zdefiniować sobie specjalną domenę do testów, której użycie będzie od razu przełączało naszą aplikację w tryb środowiska testowego. Potrzebna będzie wtedy mała zmiana w naszym głównym pliku index.php oraz zmiennej „url” w pliku „tests/acceptance.suite.yml”, tak żeby domena była ustawiona na tą, która automatycznie ustawia środowisko na testowe.

Powyższe zmiany dotyczą tylko testów akceptacyjnych. Testy jednostkowe są automatycznie uruchamiane w środowisku testowym.

To wszystko. Mamy teraz działające środowisko testowe Codeception, zintegrowane z frameworkiem CodeIgniter. Trzeba pamiętać, że zaprezentowane tutaj sposoby testowania i możliwości jakie oferuje Codeception, są bardzo podstawowe. Mamy bardzo wiele różnych opcji do dyspozycji i jeśli chcemy je bliżej poznać, musimy zagłębić się w dokumentację, która całkiem dobrze tłumaczy jak możemy testować naszą aplikację.

Kod źródłowy tego artykułu jest dostępny na GitHub (za wyjątkiem serwera Selenium, który możecie ściągnąć sobie samodzielnie).

4 thoughts on “Testy z Codeception w CodeIgniter

  1. Dzięki za wpis, pożyteczne i ciekawe rozwinięcie poprzedniego postu! Jeszcze tylko temat testów funkcjonalnych poproszę i trylogia gotowa :) Warto wytłuścić różnicę między testami – jednostkowe, funkcjonalne, akceptacyjne. Poczytałem ze strony codeception, ale to wiem tylko,że jednostkowe różnią się od funkcjonalnych tym, że te ostatnie nie wykorzystują serwera i są szybsze. No i testy z bazami danych, czy za każdym razem, żeby zrobić test jednostkowy, opróżniam bazę danych?

    • Uzupełniłem wpis o krótki opis rodzajów testów.

      Testy funkcjonalne zostały przeze mnie pominięte, ponieważ Codeception nie posiada oficjalnego modułu dla CI. Bez tego takie testy trochę mijają się z celem, bo przy testach funkcjonalnych powinniśmy mieć możliwość sprawdzenia stanu niektorych elemetów frameworka. Co prawda znalazłem nieoficjalny moduł, ale wpis był już na tyle długi, że nie chciałem już tego tutaj przedstawiać (zresztą sam moduł jest dosyć ubogi). Dla zainteresowanych link: https://github.com/luka-zitnik/CodeIgniterModule
      Swoją drogą, testy funkcjonalne w CI są dosyć problematyczne, ponieważ w niektórych sytuacjach używamy funkcji „exit”…

      Przy testach jednostkowych zazwyczaj tylko raz ładuję testową bazę, albo wcale (Mockery).

  2. Pytanie: Jak powinien wyglądać MY_Router jeżeli mamy codeigniter zintegrowamy z modułem HMVC ? Czy możemy bez problemu testować wszystkie pliki z modułów ??

    • Z tego co widzę, w takim wypadku żadne zmiany nie są potrzebne – można pominąć całkowicie krok z klasą MY_Router.

Dodaj komentarz

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.