SlideShare a Scribd company logo
1 of 33
Download to read offline
Testy API
połączenie z bazą danych czy implementacja w pamięci
Krystian Żądło (koddlo.pl)
● Testy API, czyli co…
● Testowanie kodu wymagającego źródła danych
● Baza danych dla testów
○ Zady i Walety
○ Przykład w PHP
● Implementacja w pamięci
○ Zady i Walety
○ Przykład w PHP
● Porównanie rozwiązań
● Podsumowanie
● Bonus
● Q&A
Spis treści
Testy API, czyli co…
Testowanie kodu
wymagającego źródła
danych
Baza danych dla testów
Zady i Walety
+ najbardziej zbliżone do produkcji
+ krótki czas implementacji
+ łatwy development
+ łatwe do wprowadzenia w legacy
- bardzo wolne i zasobożerne
- rzadko uruchamiane
final class UserAPITest extends WebTestCase
{
private KernelBrowser $client;
private UserFixture $fixtures;
public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void {...}
public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void {...}
protected function setUp(): void {...}
}
Przykład w PHP
protected function setUp(): void
{
$this→client = static::createClient();
$this→fixtures = $this→client→getContainer()→get(UserFixture::class);
$entityManager = $this→client→getContainer()→get(EntityManagerInterface::class);
$metadata = $entityManager→getMetadataFactory()→getAllMetadata();
$schemaTool = new SchemaTool($entityManager);
$schemaTool→dropSchema($metadata);
$schemaTool→createSchema($metadata);
}
Przykład w PHP
public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void
{
$body = [
'name' ⇒ 'John Doe',
'email' ⇒ 'john.doe@example.com'
];
$this→client→jsonRequest('POST', 'users', $body);
$this→assertResponseStatusCodeSame(Response::HTTP_CREATED);
}
Przykład w PHP
public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void
{
$this→fixtures→load();
$expectedContent = '
[
{
"id": "4a4e03b2-5acc-4693-9d1f-dbc2d5662ee4",
"email": "john.doe@example.com",
"name": "John Doe"
},
{
"id": "3b3e03b2-5acc-4693-9d1f-dbc2d5662ff1",
"email": "jane.dawson@example.com",
"name": "Jane Dawson"
}
]
';
$this→client→request('GET', 'users');
$this→assertResponseStatusCodeSame(Response::HTTP_OK);
$this→assertJsonStringEqualsJsonString($expectedContent, $this→client→getResponse()→getContent());
}
Przykład w PHP
Implementacja w pamięci
Zady i Walety
+ bardzo szybkie
+ często uruchamiane
+ wymusza dobre praktyki
+ łatwo się z nich wycofać
- mniejsze pewność i wiarygodność
- trudny i wolny development
- dużo dodatkowego kodu
- trudne do wprowadzenia w legacy
services_test.yaml
PresentationDomainUserRepositoryInterface: '@PresentationTestsUtilsRepositoryUserInMemoryRepository'
PresentationApplicationQueryListUsersQueryInterface: '@PresentationTestsUtilsQueryListUsersInMemoryQuery'
PresentationApplicationQueryUniqueEmailQueryInterface: '@PresentationTestsUtilsQueryUniqueEmailInMemoryQuery'
Przykład w PHP
interface UserRepositoryInterface
{
public function save(User $user): void;
}
final class UserRepository implements UserRepositoryInterface
{
public function __construct(private EntityManager $entityManager) {}
public function save(User $user): void
{
$this→entityManager→persist($user);
$this→entityManager→flush();
}
}
Przykład w PHP
final class UserInMemoryRepository implements UserRepositoryInterface
{
private array $users = [];
public function save(User $user): void
{
$this→users[$user→getId()→toString()] = $user;
}
public function getOneByEmail(string $email): User
{
foreach ($this→users as $user) {
if ($email === $user→getEmail()→toString()) {
return $user;
}
}
throw new UserNotFoundException();
}
public function findAll(): array
{
return array_values($this→users);
}
}
Przykład w PHP
interface ListUsersQueryInterface
{
/** @return User[] */
public function execute(): array;
}
final class ListUsersInMemoryQuery implements ListUsersQueryInterface
{
public function __construct(private UserInMemoryRepository $repository) {}
public function execute(): array
{
return array_map(
fn (User $user) ⇒ new DTO(
$user→getId()→toString(),
$user→getEmail()→toString(),
$user→getName()→toString()
),
$this→repository→findAll()
);
}
}
Przykład w PHP
interface UniqueEmailQueryInterface
{
public function execute(string $email, ?string $contextId = null): bool;
}
final class UniqueEmailInMemoryQuery implements UniqueEmailQueryInterface
{
public function __construct(private UserInMemoryRepository $repository) {}
public function execute(string $email, ?string $contextId = null): bool
{
try {
$user = $this→repository→getOneByEmail($email);
} catch (UserNotFoundException) {
return true;
}
return (string) $contextId === $user→getId()→toString();
}
}
Przykład w PHP
final class UserAPITest extends WebTestCase
{
private KernelBrowser $client;
private UserFixture $fixtures;
public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void {...}
public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void {...}
protected function setUp(): void {...}
}
Przykład w PHP
protected function setUp(): void
{
$this→client = static::createClient();
$this→fixtures = $this→client→getContainer()→get(UserFixture::class);
}
Przykład w PHP
public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void
{
$body = [
'name' ⇒ 'John Doe',
'email' ⇒ 'john.doe@example.com'
];
$this→client→jsonRequest('POST', 'users', $body);
$this→assertResponseStatusCodeSame(Response::HTTP_CREATED);
}
Przykład w PHP
public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void
{
$this→fixtures→load();
$expectedContent = '
[
{
"id": "4a4e03b2-5acc-4693-9d1f-dbc2d5662ee4",
"email": "john.doe@example.com",
"name": "John Doe"
},
{
"id": "3b3e03b2-5acc-4693-9d1f-dbc2d5662ff1",
"email": "jane.dawson@example.com",
"name": "Jane Dawson"
}
]
';
$this→client→request('GET', 'users');
$this→assertResponseStatusCodeSame(Response::HTTP_OK);
$this→assertJsonStringEqualsJsonString($expectedContent, $this→client→getResponse()→getContent());
}
Przykład w PHP
Porównanie rozwiązań
Porównanie rozwiązań
W pamięci
PHPUnit 9.5.20
........................................ 40 / 40 (100%)
Time: 00:00.637, Memory: 22.00 MB
OK (40 tests, 100 assertions)
Baza danych
PHPUnit 9.5.20
........................................ 40 / 40 (100%)
Time: 03:05.990, Memory: 36.00 MB
OK (40 tests, 100 assertions)
Rollback
Sqlite i inne tego typu
Podsumowanie
Bonus
public function testInvoke_whenIsCalled_shouldAddNewUser(): void
{
$command = new CreateUserCommand(
'1f1450f0-e016-4ddd-a221-39c933bc5ac1',
'john.doe@example.com',
'John',
'Doe',
'password'
);
$repository = $this→prophesize(UserRepositoryInterface::class);
$SUT = new CreateUserHandler($repository→reveal());
$SUT($command);
$repository→save(Argument::type(User::class))→shouldHaveBeenCalled();
}
In Memory w testach jednostkowych
public function testInvoke_whenIsCalled_shouldAddNewUser(): void
{
$id = '1f1450f0-e016-4ddd-a221-39c933bc5ac1';
$command = new CreateUserCommand(
$id,
'john.doe@example.com',
'John',
'Doe',
'password'
);
$repository = new UserInMemoryRepository();
$SUT = new CreateUserHandler($repository);
$SUT($command);
self::assertInstanceOf(User::class, $repository→get(new UserId($id)));
}
In Memory w testach jednostkowych
Q&A

More Related Content

Similar to Testy API: połączenie z bazą danych czy implementacja w pamięci

REST API - teoria i praktyka - WordUp Trójmiasto
REST API - teoria i praktyka - WordUp TrójmiastoREST API - teoria i praktyka - WordUp Trójmiasto
REST API - teoria i praktyka - WordUp TrójmiastoTomasz Dziuda
 
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Codesushi.co (CODESUSHI LLC)
 
Angular restmod – wygodny sposób na komunikację z API
Angular restmod – wygodny sposób na komunikację z APIAngular restmod – wygodny sposób na komunikację z API
Angular restmod – wygodny sposób na komunikację z APIThe Software House
 
4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing
4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing
4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future ProcessingPROIDEA
 
Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8
Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8
Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8Szymon Stępniak
 
Znaki mocy dla laików – Programowanie funkcyjne w JavaScript
Znaki mocy dla laików – Programowanie funkcyjne w JavaScriptZnaki mocy dla laików – Programowanie funkcyjne w JavaScript
Znaki mocy dla laików – Programowanie funkcyjne w JavaScriptThe Software House
 
Metaprogramowanie w JS
Metaprogramowanie w JSMetaprogramowanie w JS
Metaprogramowanie w JSDawid Rusnak
 
O wezu przy kawie
O wezu przy kawieO wezu przy kawie
O wezu przy kawiekraqa
 
Automatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHPAutomatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHPLaravel Poland MeetUp
 
Red Green Hotfix – złudne poczucie bezpieczeństwa w testach
Red Green Hotfix – złudne poczucie bezpieczeństwa w testachRed Green Hotfix – złudne poczucie bezpieczeństwa w testach
Red Green Hotfix – złudne poczucie bezpieczeństwa w testachThe Software House
 
Jarorcon Sp
Jarorcon SpJarorcon Sp
Jarorcon Spjarorcon
 
AADays 2015 - Jak to zrobic w JavaScript
AADays 2015 - Jak to zrobic w JavaScriptAADays 2015 - Jak to zrobic w JavaScript
AADays 2015 - Jak to zrobic w JavaScriptJacek Okrojek
 
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016PROIDEA
 
Functional widgets in Rails
Functional widgets in RailsFunctional widgets in Rails
Functional widgets in RailsSebastian Sito
 
Professional Javascript for Developers
Professional  Javascript for DevelopersProfessional  Javascript for Developers
Professional Javascript for DevelopersRule_Financial
 
Architektura to nie bzdura
Architektura to nie bzduraArchitektura to nie bzdura
Architektura to nie bzduraPawel Szulc
 

Similar to Testy API: połączenie z bazą danych czy implementacja w pamięci (20)

REST API - teoria i praktyka - WordUp Trójmiasto
REST API - teoria i praktyka - WordUp TrójmiastoREST API - teoria i praktyka - WordUp Trójmiasto
REST API - teoria i praktyka - WordUp Trójmiasto
 
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
 
Angular restmod – wygodny sposób na komunikację z API
Angular restmod – wygodny sposób na komunikację z APIAngular restmod – wygodny sposób na komunikację z API
Angular restmod – wygodny sposób na komunikację z API
 
4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing
4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing
4Developers 2015: .NET 2015 - co nowego? - Michał Dudak, Future Processing
 
Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8
Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8
Toruń JUG - Wprowadzenie do wybranych zagadnień JDK 8
 
Znaki mocy dla laików – Programowanie funkcyjne w JavaScript
Znaki mocy dla laików – Programowanie funkcyjne w JavaScriptZnaki mocy dla laików – Programowanie funkcyjne w JavaScript
Znaki mocy dla laików – Programowanie funkcyjne w JavaScript
 
Metaprogramowanie w JS
Metaprogramowanie w JSMetaprogramowanie w JS
Metaprogramowanie w JS
 
O wezu przy kawie
O wezu przy kawieO wezu przy kawie
O wezu przy kawie
 
Automatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHPAutomatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHP
 
react-pl.pdf
react-pl.pdfreact-pl.pdf
react-pl.pdf
 
Android i REST
Android i RESTAndroid i REST
Android i REST
 
Red Green Hotfix – złudne poczucie bezpieczeństwa w testach
Red Green Hotfix – złudne poczucie bezpieczeństwa w testachRed Green Hotfix – złudne poczucie bezpieczeństwa w testach
Red Green Hotfix – złudne poczucie bezpieczeństwa w testach
 
Wprowadzenie do PHPUnit
Wprowadzenie do PHPUnitWprowadzenie do PHPUnit
Wprowadzenie do PHPUnit
 
Jarorcon Sp
Jarorcon SpJarorcon Sp
Jarorcon Sp
 
DSL - DYI
DSL - DYIDSL - DYI
DSL - DYI
 
AADays 2015 - Jak to zrobic w JavaScript
AADays 2015 - Jak to zrobic w JavaScriptAADays 2015 - Jak to zrobic w JavaScript
AADays 2015 - Jak to zrobic w JavaScript
 
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
Konrad Kokosa - Pamięć w .NET - od ogólu do szczegółu- 4developers2016
 
Functional widgets in Rails
Functional widgets in RailsFunctional widgets in Rails
Functional widgets in Rails
 
Professional Javascript for Developers
Professional  Javascript for DevelopersProfessional  Javascript for Developers
Professional Javascript for Developers
 
Architektura to nie bzdura
Architektura to nie bzduraArchitektura to nie bzdura
Architektura to nie bzdura
 

More from The Software House

Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...The Software House
 
Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?The Software House
 
O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?The Software House
 
Chat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon ChimeChat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon ChimeThe Software House
 
Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?The Software House
 
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWSAnaliza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWSThe Software House
 
Feature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScriptFeature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScriptThe Software House
 
Typowanie nominalne w TypeScript
Typowanie nominalne w TypeScriptTypowanie nominalne w TypeScript
Typowanie nominalne w TypeScriptThe Software House
 
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQLAutomatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQLThe Software House
 
Serverless Compose vs hurtownia danych
Serverless Compose vs hurtownia danychServerless Compose vs hurtownia danych
Serverless Compose vs hurtownia danychThe Software House
 
Jak skutecznie read model. Case study
Jak skutecznie read model. Case studyJak skutecznie read model. Case study
Jak skutecznie read model. Case studyThe Software House
 
Firestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny KrzemowejFirestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny KrzemowejThe Software House
 
Jak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzachJak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzachThe Software House
 
O łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.jsO łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.jsThe Software House
 
Amazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurzeAmazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurzeThe Software House
 
Od Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki koduOd Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki koduThe Software House
 
Co QA może i czego nie powinien się bać?
Co QA może i czego nie powinien się bać?Co QA może i czego nie powinien się bać?
Co QA może i czego nie powinien się bać?The Software House
 

More from The Software House (20)

Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
 
Uszanowanko Podsumowanko
Uszanowanko PodsumowankoUszanowanko Podsumowanko
Uszanowanko Podsumowanko
 
Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?
 
O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?
 
Chat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon ChimeChat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon Chime
 
Migracje danych serverless
Migracje danych serverlessMigracje danych serverless
Migracje danych serverless
 
Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?
 
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWSAnaliza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
 
Feature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScriptFeature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScript
 
Typowanie nominalne w TypeScript
Typowanie nominalne w TypeScriptTypowanie nominalne w TypeScript
Typowanie nominalne w TypeScript
 
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQLAutomatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
 
Serverless Compose vs hurtownia danych
Serverless Compose vs hurtownia danychServerless Compose vs hurtownia danych
Serverless Compose vs hurtownia danych
 
Jak skutecznie read model. Case study
Jak skutecznie read model. Case studyJak skutecznie read model. Case study
Jak skutecznie read model. Case study
 
Firestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny KrzemowejFirestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny Krzemowej
 
Jak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzachJak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzach
 
Jak poskromić AWS?
Jak poskromić AWS?Jak poskromić AWS?
Jak poskromić AWS?
 
O łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.jsO łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.js
 
Amazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurzeAmazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurze
 
Od Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki koduOd Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki kodu
 
Co QA może i czego nie powinien się bać?
Co QA może i czego nie powinien się bać?Co QA może i czego nie powinien się bać?
Co QA może i czego nie powinien się bać?
 

Testy API: połączenie z bazą danych czy implementacja w pamięci

  • 1. Testy API połączenie z bazą danych czy implementacja w pamięci Krystian Żądło (koddlo.pl)
  • 2. ● Testy API, czyli co… ● Testowanie kodu wymagającego źródła danych ● Baza danych dla testów ○ Zady i Walety ○ Przykład w PHP ● Implementacja w pamięci ○ Zady i Walety ○ Przykład w PHP ● Porównanie rozwiązań ● Podsumowanie ● Bonus ● Q&A Spis treści
  • 4.
  • 6. Baza danych dla testów
  • 7. Zady i Walety + najbardziej zbliżone do produkcji + krótki czas implementacji + łatwy development + łatwe do wprowadzenia w legacy - bardzo wolne i zasobożerne - rzadko uruchamiane
  • 8.
  • 9. final class UserAPITest extends WebTestCase { private KernelBrowser $client; private UserFixture $fixtures; public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void {...} public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void {...} protected function setUp(): void {...} } Przykład w PHP
  • 10. protected function setUp(): void { $this→client = static::createClient(); $this→fixtures = $this→client→getContainer()→get(UserFixture::class); $entityManager = $this→client→getContainer()→get(EntityManagerInterface::class); $metadata = $entityManager→getMetadataFactory()→getAllMetadata(); $schemaTool = new SchemaTool($entityManager); $schemaTool→dropSchema($metadata); $schemaTool→createSchema($metadata); } Przykład w PHP
  • 11. public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void { $body = [ 'name' ⇒ 'John Doe', 'email' ⇒ 'john.doe@example.com' ]; $this→client→jsonRequest('POST', 'users', $body); $this→assertResponseStatusCodeSame(Response::HTTP_CREATED); } Przykład w PHP
  • 12. public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void { $this→fixtures→load(); $expectedContent = ' [ { "id": "4a4e03b2-5acc-4693-9d1f-dbc2d5662ee4", "email": "john.doe@example.com", "name": "John Doe" }, { "id": "3b3e03b2-5acc-4693-9d1f-dbc2d5662ff1", "email": "jane.dawson@example.com", "name": "Jane Dawson" } ] '; $this→client→request('GET', 'users'); $this→assertResponseStatusCodeSame(Response::HTTP_OK); $this→assertJsonStringEqualsJsonString($expectedContent, $this→client→getResponse()→getContent()); } Przykład w PHP
  • 14. Zady i Walety + bardzo szybkie + często uruchamiane + wymusza dobre praktyki + łatwo się z nich wycofać - mniejsze pewność i wiarygodność - trudny i wolny development - dużo dodatkowego kodu - trudne do wprowadzenia w legacy
  • 15.
  • 17. interface UserRepositoryInterface { public function save(User $user): void; } final class UserRepository implements UserRepositoryInterface { public function __construct(private EntityManager $entityManager) {} public function save(User $user): void { $this→entityManager→persist($user); $this→entityManager→flush(); } } Przykład w PHP
  • 18. final class UserInMemoryRepository implements UserRepositoryInterface { private array $users = []; public function save(User $user): void { $this→users[$user→getId()→toString()] = $user; } public function getOneByEmail(string $email): User { foreach ($this→users as $user) { if ($email === $user→getEmail()→toString()) { return $user; } } throw new UserNotFoundException(); } public function findAll(): array { return array_values($this→users); } } Przykład w PHP
  • 19. interface ListUsersQueryInterface { /** @return User[] */ public function execute(): array; } final class ListUsersInMemoryQuery implements ListUsersQueryInterface { public function __construct(private UserInMemoryRepository $repository) {} public function execute(): array { return array_map( fn (User $user) ⇒ new DTO( $user→getId()→toString(), $user→getEmail()→toString(), $user→getName()→toString() ), $this→repository→findAll() ); } } Przykład w PHP
  • 20. interface UniqueEmailQueryInterface { public function execute(string $email, ?string $contextId = null): bool; } final class UniqueEmailInMemoryQuery implements UniqueEmailQueryInterface { public function __construct(private UserInMemoryRepository $repository) {} public function execute(string $email, ?string $contextId = null): bool { try { $user = $this→repository→getOneByEmail($email); } catch (UserNotFoundException) { return true; } return (string) $contextId === $user→getId()→toString(); } } Przykład w PHP
  • 21. final class UserAPITest extends WebTestCase { private KernelBrowser $client; private UserFixture $fixtures; public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void {...} public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void {...} protected function setUp(): void {...} } Przykład w PHP
  • 22. protected function setUp(): void { $this→client = static::createClient(); $this→fixtures = $this→client→getContainer()→get(UserFixture::class); } Przykład w PHP
  • 23. public function testGivenValidPayloadWhenCreatingUserShouldReturn201(): void { $body = [ 'name' ⇒ 'John Doe', 'email' ⇒ 'john.doe@example.com' ]; $this→client→jsonRequest('POST', 'users', $body); $this→assertResponseStatusCodeSame(Response::HTTP_CREATED); } Przykład w PHP
  • 24. public function testGivenNoParametersWhenListingUsersShouldReturn200WithAllUsers(): void { $this→fixtures→load(); $expectedContent = ' [ { "id": "4a4e03b2-5acc-4693-9d1f-dbc2d5662ee4", "email": "john.doe@example.com", "name": "John Doe" }, { "id": "3b3e03b2-5acc-4693-9d1f-dbc2d5662ff1", "email": "jane.dawson@example.com", "name": "Jane Dawson" } ] '; $this→client→request('GET', 'users'); $this→assertResponseStatusCodeSame(Response::HTTP_OK); $this→assertJsonStringEqualsJsonString($expectedContent, $this→client→getResponse()→getContent()); } Przykład w PHP
  • 26. Porównanie rozwiązań W pamięci PHPUnit 9.5.20 ........................................ 40 / 40 (100%) Time: 00:00.637, Memory: 22.00 MB OK (40 tests, 100 assertions) Baza danych PHPUnit 9.5.20 ........................................ 40 / 40 (100%) Time: 03:05.990, Memory: 36.00 MB OK (40 tests, 100 assertions)
  • 28. Sqlite i inne tego typu
  • 30. Bonus
  • 31. public function testInvoke_whenIsCalled_shouldAddNewUser(): void { $command = new CreateUserCommand( '1f1450f0-e016-4ddd-a221-39c933bc5ac1', 'john.doe@example.com', 'John', 'Doe', 'password' ); $repository = $this→prophesize(UserRepositoryInterface::class); $SUT = new CreateUserHandler($repository→reveal()); $SUT($command); $repository→save(Argument::type(User::class))→shouldHaveBeenCalled(); } In Memory w testach jednostkowych
  • 32. public function testInvoke_whenIsCalled_shouldAddNewUser(): void { $id = '1f1450f0-e016-4ddd-a221-39c933bc5ac1'; $command = new CreateUserCommand( $id, 'john.doe@example.com', 'John', 'Doe', 'password' ); $repository = new UserInMemoryRepository(); $SUT = new CreateUserHandler($repository); $SUT($command); self::assertInstanceOf(User::class, $repository→get(new UserId($id))); } In Memory w testach jednostkowych
  • 33. Q&A