Testy akceptacyjne, funkcjonalne, jednostkowe – czym się różnią?

Jak przetestować naszą aplikację? Mamy kilka możliwości. Poznajmy najpopularniejsze rodzaje testów, ich przeznaczenie i różnice.

Testy Akceptacyjne (Acceptance testing)

Najprostszy sposób testowania. Polega na przeklikaniu aplikacji według zadanego scenariusza. Sprawdzamy czy wszystko gra i buczy.

W testach akceptacyjnych udajemy użytkownika. Nie interesuje nas co siedzi w środku. Sprawdzamy czy aplikacja zachowuje się tak jak powinna. Możemy to robić ręcznie lub za pomocą automatów.

Przykładowo Codeception to środowisko do pisania fajnych testów akceptacyjnych. W PHP tworzymy plik z testem gdzie definiujemy aktora (wirtualną osobę, która przeklikuje) po czym klikamy po stronie. Kod jest bajecznie prosty:

// tworzymy wirtualnego testera (pod zmienną $I czyli $Ja)
$I = new AcceptanceTester($scenario);
$I->wantTo('check if blog works');

// sprawdzamy czy widać tytuł na stronie
$I->amOnPage('http://adam.wroclaw.pl');
$I->see('/www/');

// klikamy w link "O mnie" i sprawdzamy czy jest właściwy tekst
$I->click('O mnie');
$I->see('Moja historia');

// wyszukujemy artykuły o Oauth2 i sprawdzamy wyniki
$I->fillField('input[type="search"]','Oauth2');
$I->submitForm('form[role="search"]',[]);
$I->see('OAuth2 wyjaśniony po ludzku');

Kiedy uruchomimy nasz test, codeception powie nam:

Check if blog works (BlogCept)
Scenario:
* I am on page "http://adam.wroclaw.pl"
* I see "/www/"
* I click "O mnie"
* I see "Moja historia"
* I fill field "input[type="search"]","Oauth2"
* I submit form "form[role="search"]",[]
* I see "OAuth2 wyjaśniony po ludzku"
 PASSED

Możemy w ten sposób dowolnie klikac po stronach, testować formularze i zachowanie naszego serwisu. Możemy testować też obce serwisy. :)

Testy funkcjonalne (Black-box testing)

Nazywamy je testami czarnej skrzynki. Bierzemy fragment systemu po czym karmimy go danymi i sprawdzamy czy zwraca to, co powinien.

Taki fragment aplikacji to pojedynczy serwis, strona z formularzem, obiekt, biblioteka itp. Nie interesuje nas co jest w środku. Traktujemy go jako czarną skrzynkę i sprawdzamy jej zachowanie.

Przyjmijmy, że mamy serwis – wyszukiwarkę. Podajemy mu hasła i spodziewamy się wyników. Test możemy napisać np. w PHPUnit (w przykładzie wpiętym w Symfony):

namespace SomeBundle\Tests;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class SearchTest extends KernelTestCase
{
  // nasz serwis z wyszukiwaniem
  private $searchService;

  // pobieramy serwis z kontenera
  protected function setUp()
  {
    self::bootKernel();
    $this->searchService = static::$kernel->getContainer()->get('my.search.service');
  }

  // testujemy wyszukiwanie
  public function testSearchForKittens()
  {
    $results = $this->searchService->search('kot');
    foreach ($results as $item) {
      $this->assertInstanceOf(\SomeBundle\Entity\Cat,$item);
    }
  }
}

W powyższym przypadku nie interesuje nas, czy serwis szuka w lokalnej bazie, łączy się z innym zewnętrznym serwisem czy też generuje dane. Ma zwracać dane dobrego typu. Wszystko co w środku jest czarną skrzynką.

Testy Jednostkowe (Unit testing)

Tutaj testujemy pojedyncze obiekty, funkcje, metody. Wiemy co jest w środku, wiemy jak metoda ma się zachować. Chcemy sprawdzić, czy działa jak powinna. Co ważne, chcemy to sprawdzić szybko.

Często używamy tych testów w TDD (Test Driven Developement). To bardzo wygodna forma programowania. Najpierw piszemy test a potem programujemy funkcję, która ma go spełniać. Przykładowo (znów w PHPUnit):

class UnitTest extends \PHPUnit_Framework_TestCase
{
  // czy w systemie mamy koty
  public function testClassExists()
  {
    $object = new \Cat();
    $this->assertInstanceOf(\Cat,$object);
  }
}

Puszczamy test… oczywiście zwraca błąd. Tworzymy więc naszą klasę \Cat aż test przejdzie. Potem dopisujemy kolejny test:

class UnitTest extends \PHPUnit_Framework_TestCase
{
  // ... poprzednie metody testujące

  // czy koty miauczą
  public function testCanMeyow()
  {
    $object = new \Cat();
    $result = $object->meyow()
    $this->assertTrue($result);
  }
}

Oczywiście test nie przejdzie. Musimy dopisać metodę meyow(). Gdy już będzie, piszemy kolejny test i kolejny.

Po co? Na początku wydaje się głupie. Ale ile razy w starym kodzie baliśmy się coś zmienić żeby nie zepsuć? Pamiętamy stare powiedzenia „działa? to nie poprawiaj” albo „lepsze jest wrogiem dobrego„. Jeśli mamy testy jednostkowe, możemy zmieniać bez strachu. Testy powiedzą, czy coś napsuliśmy.

Testy jednostkowe muszą uruchamiać się szybko. Powinny trwać kilka sekund. Jeśli w takich testach zaczniemy łączyć się z bazą, wysyłać wiadomości itp – to już stają się testami funkcjonalnymi.

Co dalej?

Granica między tymi testami nie jest sztywna. Test jednostkowy gdy zacznie obejmować większy fragment aplikacji staje się testem funkcjonalnym. Jeśli napiszemy test funkcjonalny odwołujący się do całej strony (np. sprawdzający headery) – stąd już krok do testu akceptacyjnego.

Dwa najpopularniejsze narzędzia do testów w PHP to PHPUnit oraz Codeception. To drugie rozumie pliki PHPUnit oraz może wykonywać wszystkie opisane tutaj rodzaje testów.

Jest jeszcze dużo innych rodzajów testów – smoke testy, regresyjne, integracyjne, recovery testing, testy mutacyjne i wiele wiele innych.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *