W swoim poprzednim wpisie pokazałem podstawy konfiguracji zestawu narzędzi Behat+PHPUnit umożliwiający tworzenie scenariuszy przypadków uzycia. Konfiguracja ta pozwalała następnie na implementację tych scenariuszy oraz przetestowanie backendu. W dzisiejszym wpisie chciałbym pokazać, jak skonfigurować Behata do wykonywania testów funkcjonalnych, tak aby móc testować warstwę, którą widzi użytkownik.
Podobnie jak ostatnio, na początku musimy zainstalować w naszym systemie menedżer zależności Composer. Jeżeli jest on już zainstalowany, to świetnie! Stwórzmy zatem nowy projekt i dodajmy w nim plik composer.json z zawartością:
{
"require-dev": {
"behat/behat": "^3.4",
"behat/mink-selenium2-driver": "^1.3",
"behat/mink-extension": "^2.3",
"behat/mink": "^1.7"
},
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
}
Następnie dodajemy do głównego katalogu folder app (na pliki źródłowe zawierające logikę) oraz instalujemy zależności poleceniem:
composer install
W tym odcinku wykonywamy analogiczny projekt skryptu obliczającego ilość permutacji zbioru liczb. W głównym katalogu projektu tworzymy plik behat.yaml z konfiguracją Behata:
default:
suites:
permutation:
paths: [ features/app ]
contexts: [ PermutationContext ]
extensions:
Behat\MinkExtension:
base_url: http://localhost/index.php
browser_name: 'chrome'
sessions:
default:
selenium2:
wd_host: 'http://localhost:4444/wd/hub'
capabilities: {}
Jak zapewne widzisz, tym razem doszedł zestaw ustawień pod kluczem Behat\MinkExtension. Zawiera on wybraną przeglądarkę, zmienną zawierającą adres strony głównej aplikacji oraz adres, pod którym nasłuchiwać będzie serwer. Inicjujemy Behata:
.\vendor\bin\behat --init
W katalogu featues/app tworzymy plik permutation.feature i dodajemy zawartość:
# language: pl
Funkcja: Zliczanie ilości permutacji zbioru liczb
Szablon scenariusza: Użytkownik zlicza ilość permutacji zbioru liczb naturalnych dodatnich
Zakładając użytkownik chce otrzymać ilość permutacji wprowadzonego zbioru
Oraz użytkownik dodaje do skryptu "<n>" liczb naturalnych dodatnich
Wtedy użytkownik otrzymuje liczbę "<m>"
Przykłady:
| n | m |
| 5 | 120 |
| 4 | 24 |
Różni się on od tego ostatnio. W tym przypadku mamy szablon scenariusza, w którym znajdują się zmienne n oraz m. Następnie tworzymy dla przygotowanego scenariusza kontekst poleceniem:
.\vendor\bin\behat --dry-run --append-snippets
Został utworzony plik features/boostrap/PermutationContext.php z zawartoscią:
<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
/**
* Defines application features from the specific context.
*/
class PermutationContext implements Context
{
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* @When użytkownik dodaje do skryptu :arg1 liczb naturalnych dodatnich
*/
public function uzytkownikDodajeDoSkryptuLiczbNaturalnychDodatnich($arg1)
{
throw new PendingException();
}
/**
* @When użytkownik chce otrzymać ilość permutacji wprowadzonego zbioru
*/
public function uzytkownikChceOtrzymacIloscPermutacjiWprowadzonegoZbioru()
{
throw new PendingException();
}
/**
* @Then użytkownik otrzymuje liczbę :arg1
*/
public function uzytkownikOtrzymujeLiczbe($arg1)
{
throw new PendingException();
}
}
Zostawmy go w takim stanie przez chwilę. Utwórzmy teraz implementację założonej na wstępie funkcjonalności. Tworzymy klasę Permutation.php w folderze app:
<?php
namespace App;
class Permutation
{
private $numbers = [];
public function add(array $numbers): void
{
$this->numbers = array_merge($this->numbers, $numbers);
}
public function getDataSet(): array
{
return $this->numbers;
}
public function count()
{
$itemsCount = count($this->numbers);
if ($itemsCount > 0) {
$permutationCount = $itemsCount;
while ($itemsCount-- && $itemsCount !== 0) $permutationCount *= $itemsCount;
return $permutationCount;
} else {
return 1;
}
}
}
W głównym folderze zróbmy skrypt wykonujący. Stwórzmy plik index.php:
<?php
require './vendor/autoload.php';
session_start();
$template = file_get_contents('./views/index.tpl');
$action = $_POST['action'];
$number = $_POST['permutation-number'];
switch ($action) {
case 'add':
$instance = $_SESSION['permutation']['instance'];
if (!empty($number)) {
$instance->add([$number]);
}
$dataSet = implode(", ", $instance->getDataSet());
$count = 0;
break;
case 'get':
$instance = $_SESSION['permutation']['instance'];
$dataSet = implode(", ", $instance->getDataSet());
$count = $instance->count();
break;
default:
$_SESSION['permutation']['instance'] = new App\Permutation();
break;
}
$pageContent = str_replace(
['{$permutationNumber}', '{$permutationSet}', '{$permutationCount}'],
[$number, $dataSet, $count], $template);
echo $pageContent;
Skrypt jest ten trochę chaotyczny, ale myślę że prosty w zrozumieniu. Tworzymy sesję, w której trzymamy dane. Pobieramy z formularza dane i je przetwarzamy. Następnie wypełniamy danymi widok. Widok znajduje się pod ścieżką views/index.tpl, którą oczywiście musimy utworzyć. Wypełniamy plik index.tpl zawartością:
<style>
input, textarea {
display: block;
margin-bottom: 10px;
width: 300px;
resize: none;
}
</style>
<form action="index.php" method="post">
<label for="permutation-number">Liczba</label>
<input id="permutation-number" name="permutation-number" type="number" value="{$permutationNumber}" />
<label for="permutation-set">Zbiór liczb</label>
<textarea id="permutation-set" name="permutation-set" type="number" height="2" readonly>{$permutationSet}</textarea>
<label for="permutation-count">Liczba permutacji</label>
<input id="permutation-count" name="permutation-count" type="number" readonly value="{$permutationCount}" />
<button id="permutation-add" name="action" type="submit" value="add">Add number to set</button>
<button id="permutation-get" name="action" type="submit" value="get">Get permutation count</button>
</form>
Jak widać jest on bardzo prosty, nie zawiera nawet zestawu typowych znaczników. Dodatkowo style przechowywane są w tym samy pliku, co nie jest dobrą praktyką. Mamy już gotową aplikację, możemy ją przetestować w przeglądarce. Teraz pora na uzupełnienie kontekstu dla scenariusza. Plik PermutationContext.php uzupełniamy metodami:
<?php
use Behat\Mink\WebAssert;
use Behat\MinkExtension\Context\MinkContext;
class PermutationContext extends MinkContext
{
/**
* @When użytkownik dodaje do skryptu :arg1 liczb naturalnych dodatnich
*/
public function uzytkownikDodajeDoSkryptuLiczbNaturalnychDodatnich($arg1)
{
$input = $this->getSession()->getPage()->findById('permutation-number');
$button = $this->getSession()->getPage()->findById('permutation-add');
for ($i = 1; $i <= $arg1; $i++) {
$input->setValue($i);
$button->click();
}
}
/**
* @When użytkownik chce otrzymać ilość permutacji wprowadzonego zbioru
*/
public function uzytkownikChceOtrzymacIloscPermutacjiWprowadzonegoZbioru()
{
$url = $this->getMinkParameter('base_url');
$this->getSession()->visit($url);
}
/**
* @Then użytkownik otrzymuje liczbę :arg1
* @throws \Behat\Mink\Exception\ExpectationException
*/
public function uzytkownikOtrzymujeLiczbe($arg1)
{
$button = $this->getSession()->getPage()->findById('permutation-get');
$button->click();
(new WebAssert($this->getSession()))->fieldValueEquals('permutation-count', $arg1);
}
}
Główną różnicą jest wymiana implementacji interfejsu Context na rozszerzenie klasy MinkContext. Następnie zostały uzupełnione kroki. Jesteśmy już prawie gotowi do uruchomienia testów! Pobieramy teraz Selenium w wersji 2, np. za pomocą polecenia w konsoli:
php --run "copy('https://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar', './selenium-server-standalone-2.53.1.jar')"
Startujemy serwer Selenium, wymagane jest wcześniejsze zainstalowanie maszyny wirtualnej Java. Selenium uruchamiamy poleceniem:
java -jar selenium-server-standalone-2.53.1.jar
Serwer wystartuje i zablokuje terminal do czasu jego wyłączenia. Dlatego teraz uruchamiamy nową sesję terminala i wpisujemy komendę:
.\vendor\bin\behat
Uruchomi się przeglądarka Google Chrome i testy automatyczne się wykonają. Na koniec w konsoli pojawi się informacja o przebiegu testu. To wszystko na dzisiaj. Jak zawsze udostępniam pliki z gotowym projektem: pliki źródłowe. Pozdrawiam!
Komentarze
Ten wpis nie posiada komentarzy.
Dodaj komentarz
Pola oznaczone gwiazdką (*) są wymagane. Komentarze są wstępnie moderowane i mogą nie pojawić się na stronie.