• Czas czytania ~4 min
  • 10.09.2022

BDD, czyli Behavioral Driven Development, to popularne podejście do testowania w wielu organizacjach i ma udokumentowane doświadczenie w jednoczeniu wysiłków testowych między zespołami. Pozostaje jednak pytanie, jak możemy to osiągnąć w Laravelu bez konieczności uczenia się nowego frameworka do testowania lub nowej składni języka, takiej jak korniszon.

Jako firma, możliwość definiowania procesów w łatwy do odczytania sposób i reprezentowania tego w naszych zestawach testowych jest ogromną korzyścią. Podobnie jak Domain Driven Design pozwala nam stworzyć wszechobecny język dla naszego kodu, BDD umożliwi nam posiadanie wszechobecnego języka do naszych testów.

Przejrzyjmy kilka przykładów jak może wyglądać test BDD, a następnie przeanalizujmy to. Wyobraźmy sobie, że mamy aplikację internetową, która posiada formularz rejestracyjny. Po wypełnieniu tego formularza oczekujemy, że użytkownik zostanie zarejestrowany i powinien zostać automatycznie zalogowany. Spójrzmy na to w typowym teście funkcji:

it('allows a user to register for an account', function (string $email) {
	expect(
		User::query()->count(),
	)->toEqual(0);
 
	post(
		route('register'),
		['name' => 'test', 'email' => $email, 'password' => 'password']
	)->assertRedirect(route('dashboard'));
 
	expect(
		User::query()->count(),
	)->toEqual(1);
})->with('emails');

Jest to prosty przykład tego, co można zrobić z pestPHP, aby przetestować ten punkt końcowy, który replikuje przesyłanie formularza. Jak widzisz, jako programista jest to stosunkowo łatwe do zrozumienia, jeśli jesteś przyzwyczajony do testowania ze szkodnikami. Jednak twój inżynier QA będzie miał z tym problem, ponieważ nie jest przyzwyczajony do pestPHP i nie ma składni, którą rozumieją.

Jak możemy to zmienić, aby użyć BDD i składni, którą nasz inżynier QA i szerszy zespół może zrozumieć? Na szczęście wtyczka pestPHP pozwoli nam zastosować podejście „Given When Then”, które jest typowe w świecie BDD. To jest wtyczka Give when then i jest łatwa do rozpoczęcia. Uruchom następujące polecenie kompozytora, aby zainstalować tę wtyczkę:

composer require milroyfraser/pest-plugin-gwt --dev

Od tego miejsca możemy zacząć pisać konkretne testy dla BDD. Jedną rzeczą, o której chcemy pamiętać w tym momencie, jest to, czy chcemy zastąpić nasze testy, czy też chcemy, aby BDD ulepszyło nasz obecny zestaw testów? Chciałbym ulepszyć mój istniejący zestaw testów, aby uniknąć utraty cennych testów.

Weźmy przykład, z którym niedawno się spotkałem. Nie używałem żadnego konkretnego pakietu Auth dla mojej aplikacji Laravel. Zamiast tego musiałem utworzyć niestandardowy przepływ uwierzytelniania — przy użyciu hasła jednorazowego. Mój formularz rejestru jest komponentem Livewire, który obsługuje logikę za mnie. Więc najpierw napiszemy test funkcji, aby upewnić się, że nasz komponent działa.

it('will submit the form and create a new user', function (string $email) {
	Livewire::test(
		RegisterForm::class,
	)->set(
		'name', 'test',
	)->set(
		'email', $email,
	)->set(
		'password', 'password',
	)->call(
		'submit'
	)->assertHasNoErrors(
		['name', 'email', 'password']
	);
})->with('emails');

Testujemy, czy możemy wypełnić i przesłać formularz. Moglibyśmy dodać nasze oczekiwania w związku z tym, aby upewnić się, że użytkownik zostanie utworzony w bazie danych, ale możemy tutaj uprościć nasz test funkcji i przenieść część tej logiki do testu integracji.

W naszym przypadku, podobnie jak większość mojego kodu, wykonuję logikę w klasach Action, więc przeniesienie tego ma sens. Zazwyczaj mam pojedyncze klasy akcji dla wszystkich operacji odczytu i zapisu, które muszę wykonać, aby CLI, Web i API mogły używać całej podobnej logiki - jedyną różnicą jest to, jak jest wywoływana. W powyższym przykładzie nasz komponent Livewire wywołałby akcję utworzenia użytkownika.

A teraz spójrzmy, jak wyglądałby proces biznesowy w Korniszon składnia:

Scenario: The Register Action is handled
	Given the RegisterAction is created
	When the handle method is called
	Then a new user will be created

Wprawdzie moglibyśmy napisać to w standardowym teście i miałoby to sens dla nas jako programistów – ale jedną z zasad DDD, którą uwielbiam, jest wszechobecny język, który tworzysz – prawie jak język biznesowy.

For our BDD tests, I will create an Integration directory under test, so that I have:
Unit: Test-Driven Development
Feature: Test-Driven Development
Integration: Behavioral Driven Development

W naszym katalogu Integracje będziemy przechowywać wszystkie nasze scenariusze utworzone jako testy pestPHP za pomocą zainstalowanej wtyczki.

scenario('The RegisterAction is handled')
	->given(fn () => new RegisterAction())
	->when(fn (RegisterAction $action) => $action->handle(
		name: 'test',
		email: '[email protected]',
		password: 'password',
	))->then(fn () => assertDatabaseHas('users', [
		'name' => 'test',
		'email' => '[email protected]',
	]));

Jak widać z powyższego kodu, zrozumienie tego jest łatwe. Ma duże podobieństwa do tego, czego moglibyśmy się spodziewać w większości zestawów testów BDD - ale w ramach frameworka jesteśmy do tego przyzwyczajeni. Zwykle w wielu przypadkach możemy bezpośrednio przełożyć to na historyjkę użytkownika.

Weźmy jeszcze jeden przykład, ale tym razem zaczniemy od historyjki użytkownika:

Teraz przenieśmy to do składni Gherkin:

Scenario: A user can activate their account
	Given a new user
	When they activate their account
	Then an email is sent to confirm the activation.

Na koniec przejdźmy do pestPHP z wtyczką, którą testujemy:

scenario('A user can activate their account')
	->given(fn (): User => User::factory()->inactive()->create())
	->when(fn () => Bus::fake())
	->when(fn (User $user): User => $user->activate())
	->then(function (User $user) {
		Bus::assertDispatched(ActivateUser::class);
	});

Widać więc, że ten sposób testowania ma zalety dla Twój zestaw testowy i Twój zespół. Nie mówię, że powinieneś zawsze skorzystaj z tego podejścia – ale w przypadku tych krytycznych procesów biznesowych pozwala to zmapować proces z języka zrozumiałego dla firmy na zestaw testów, który rozumiesz bezpośrednio.

Czy masz znalazłeś inne ekscytujące sposoby na ulepszenie strategii testowania w swoich aplikacjach? Podziel się z nami swoimi przemyśleniami na Twitterze!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297