Skip to content

Testování

Projekt používá Pest PHP jako testovací framework s podporou pro unit, feature a integrační testy.

  • Testují izolované komponenty (třídy, metody)
  • Umístění: tests/Unit/
  • Používají SQLite in-memory databázi pro rychlost
  • Testují kompletní funkcionality (HTTP requesty, workflow)
  • Umístění: tests/Feature/
  • Používají MySQL testovací databázi kviz_testing
  • Testují interakci mezi komponentami
  • Umístění: tests/Integration/
  • Používají MySQL testovací databázi

Terminal window
docker compose exec -T fpm php vendor/bin/pest
Terminal window
docker compose exec -T fpm php vendor/bin/pest tests/Unit/CheckDatabaseTest.php
Terminal window
docker compose exec -T fpm php vendor/bin/pest --filter="has correct database configuration"
Terminal window
docker compose exec -T fpm php vendor/bin/pest --coverage
Terminal window
# Feature testy
docker compose exec -T fpm php vendor/bin/pest tests/Feature/
# Unit testy
docker compose exec -T fpm php vendor/bin/pest tests/Unit/

Testy používají dvě různé databázové konfigurace:

Konfigurace v phpunit.xml:

<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

Výhody:

  • ⚡ Velmi rychlé
  • 🔄 Čistý stav při každém testu
  • 📦 Žádný setup

Konfigurace v phpunit.xml:

<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_HOST" value="db"/>
<env name="DB_PORT" value="3306"/>
<env name="DB_DATABASE" value="kviz_testing"/>
<env name="DB_USERNAME" value="kviz"/>
<env name="DB_PASSWORD" value="kviz"/>

Vytvoření testovací databáze:

Terminal window
# V Docker kontejneru db
docker compose exec db mysql -u kviz -pkviz -e "CREATE DATABASE IF NOT EXISTS kviz_testing;"
# Import schema
docker compose exec db mysql -u kviz -pkviz kviz_testing < database/schema/mysql-schema.sql

<?php
use App\Models\User;
test('user can be created', function () {
$user = User::factory()->create([
'email' => 'test@example.com',
]);
expect($user->email)->toBe('test@example.com');
});
it('has correct name', function () {
$user = User::factory()->create(['name' => 'John Doe']);
expect($user->name)->toBe('John Doe');
});
use App\Modules\Region;
test('region factory creates valid data', function () {
$region = Region::factory()->create();
expect($region)
->toBeInstanceOf(Region::class)
->and($region->country_id)->toBe(1)
->and($region->hidden)->toBeFalse();
});
use App\User;
test('user can login', function () {
$user = User::factory()->create([
'password' => bcrypt('password123'),
]);
$response = $this->post('/login', [
'email' => $user->email,
'password' => 'password123',
]);
$response->assertRedirect('/dashboard');
$this->assertAuthenticatedAs($user);
});
use Database\Seeders\CountrySeeder;
use Database\Seeders\RegionSeeder;
test('regions have countries', function () {
$this->seed(CountrySeeder::class);
$this->seed(RegionSeeder::class);
expect(\App\Modules\Country::count())->toBe(2)
->and(\App\Modules\Region::count())->toBe(14);
});
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('creates user in transaction', function () {
$user = User::factory()->create();
expect(User::count())->toBe(1);
// Po testu se databáze automaticky rollbackne
});

Špatně:

$user = new User();
$user->name = 'John';
$user->email = 'john@example.com';
$user->save();

Správně:

$user = User::factory()->create([
'name' => 'John',
'email' => 'john@example.com',
]);

Špatně:

test('test1', function () { ... });

Správně:

test('user with admin role can access admin panel', function () { ... });
test('user can update profile', function () {
// Arrange
$user = User::factory()->create();
// Act
$response = $this->actingAs($user)->put('/profile', [
'name' => 'New Name',
]);
// Assert
expect($user->fresh()->name)->toBe('New Name');
$response->assertSuccessful();
});

Každý test by měl být nezávislý:

// ✅ Každý test má vlastní data
test('creates user', function () {
$user = User::factory()->create();
expect(User::count())->toBe(1);
});
test('creates another user', function () {
$user = User::factory()->create();
expect(User::count())->toBe(1); // Ne 2!
});
test('handles empty input', function () {
$result = calculateTotal([]);
expect($result)->toBe(0);
});
test('handles null values', function () {
$result = calculateTotal(null);
expect($result)->toBe(0);
});

test('debug test', function () {
$user = User::factory()->create();
dump($user->toArray()); // Vypíše data
ray($user); // Pokud používáte Ray
expect($user)->toBeInstanceOf(User::class);
});
test('stop here', function () {
$user = User::factory()->create();
dd($user); // Die and dump - zastaví test
// Tento kód se neprovede
expect($user)->toBe('something');
});
Terminal window
docker compose exec -T fpm php vendor/bin/pest -v

Terminal window
docker compose exec -T fpm php vendor/bin/pest --coverage --min=80
Terminal window
docker compose exec -T fpm php vendor/bin/pest --coverage-html coverage/

Report se vytvoří v coverage/index.html.


Testy by měly běžet v CI/CD pipeline před každým merge.

test:
stage: test
script:
- docker compose up -d db
- docker compose exec -T fpm composer install
- docker compose exec -T fpm php vendor/bin/pest
only:
- merge_requests
- dev
- master

Testy selhávají kvůli chybějící databázi

Section titled “Testy selhávají kvůli chybějící databázi”
Terminal window
# Vytvořte testovací databázi
docker compose exec db mysql -u kviz -pkviz -e "CREATE DATABASE IF NOT EXISTS kviz_testing;"
  • Používejte SQLite pro unit testy
  • Používejte RefreshDatabase místo migrate:fresh v každém testu
  • Optimalizujte factories (méně DB dotazů)
Terminal window
# Regenerujte autoload
docker compose exec fpm composer dump-autoload

Testy neprocházejí v CI, ale lokálně ano

Section titled “Testy neprocházejí v CI, ale lokálně ano”
  • Zkontrolujte environment proměnné v phpunit.xml
  • Ověřte, že CI má přístup k databázi
  • Zkontrolujte PHP verzi v CI vs lokálně