318 lines
7.1 KiB
Markdown
318 lines
7.1 KiB
Markdown
|
|
# Testing Guide for MineSeeker
|
|||
|
|
|
|||
|
|
MineSeeker-specific testing setup and workflows. For general PHPUnit/Symfony testing, see [Symfony Testing Docs](https://symfony.com/doc/current/testing.html).
|
|||
|
|
|
|||
|
|
## Quick Start
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# One-time setup
|
|||
|
|
make test-db-setup
|
|||
|
|
|
|||
|
|
# Run tests
|
|||
|
|
make test
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test Database Configuration
|
|||
|
|
|
|||
|
|
MineSeeker uses a **separate test database** (`mineseeker_test`) with automatic transaction rollback for isolated tests.
|
|||
|
|
|
|||
|
|
### Stack
|
|||
|
|
|
|||
|
|
- **PHPUnit 13** - Testing framework
|
|||
|
|
- **[Zenstruck Foundry](https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html)** - Factory library for test fixtures
|
|||
|
|
- **[DAMA Doctrine Test Bundle](https://github.com/dmaicher/doctrine-test-bundle)** - Transaction isolation (rollback after each test)
|
|||
|
|
|
|||
|
|
### Configuration
|
|||
|
|
|
|||
|
|
**`phpunit.dist.xml`**:
|
|||
|
|
```xml
|
|||
|
|
<env name="DATABASE_URL" value="postgresql://...mineseeker_test..." />
|
|||
|
|
<env name="DAMA_DISABLE_STATIC_CONNECTION" value="0" />
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**`config/bundles.php`**:
|
|||
|
|
```php
|
|||
|
|
Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['dev' => true, 'test' => true],
|
|||
|
|
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Setup
|
|||
|
|
|
|||
|
|
### One-time Setup
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
make test-db-setup
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Creates `mineseeker_test` database and runs migrations.
|
|||
|
|
|
|||
|
|
### Reset Test Database
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
make test-db-reset
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Drops and recreates test database (useful after schema changes).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Running Tests
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# All tests
|
|||
|
|
make test
|
|||
|
|
|
|||
|
|
# Specific file
|
|||
|
|
vendor/bin/phpunit tests/Controller/ProfileControllerTest.php
|
|||
|
|
|
|||
|
|
# Specific method
|
|||
|
|
vendor/bin/phpunit --filter testCreateUser
|
|||
|
|
|
|||
|
|
# With test names
|
|||
|
|
vendor/bin/phpunit --testdox
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## MineSeeker Factories
|
|||
|
|
|
|||
|
|
See **[FACTORIES.md](./FACTORIES.md)** for complete factory API.
|
|||
|
|
|
|||
|
|
### Available Factories
|
|||
|
|
|
|||
|
|
| Factory | Entity | Location |
|
|||
|
|
|---------|--------|----------|
|
|||
|
|
| `UserFactory` | Registered users | `tests/Factory/UserFactory.php` |
|
|||
|
|
| `GamerFactory` | Anonymous players | `tests/Factory/GamerFactory.php` |
|
|||
|
|
| `PlayedGameFactory` | Game records | `tests/Factory/PlayedGameFactory.php` |
|
|||
|
|
| `StepFactory` | Game moves | `tests/Factory/StepFactory.php` |
|
|||
|
|
| `GridFactory` | 16×16 game grids | `tests/Factory/GridFactory.php` |
|
|||
|
|
| `GridRowFactory` | Grid rows | `tests/Factory/GridRowFactory.php` |
|
|||
|
|
| `WebAuthnCredentialFactory` | Passkey credentials | `tests/Factory/WebAuthnCredentialFactory.php` |
|
|||
|
|
| `ContactMessageFactory` | Contact messages | `tests/Factory/ContactMessageFactory.php` |
|
|||
|
|
|
|||
|
|
### Example
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
use App\Tests\Factory\PlayedGameFactory;
|
|||
|
|
use App\Tests\WebTestCase;
|
|||
|
|
|
|||
|
|
class GameTest extends WebTestCase
|
|||
|
|
{
|
|||
|
|
public function testGameCreation(): void
|
|||
|
|
{
|
|||
|
|
$game = PlayedGameFactory::new()
|
|||
|
|
->withRegisteredPlayers()
|
|||
|
|
->redWins()
|
|||
|
|
->create();
|
|||
|
|
|
|||
|
|
self::assertEquals(26, $game->redPoints);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test Structure
|
|||
|
|
|
|||
|
|
All tests extend `App\Tests\WebTestCase`:
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
<?php declare(strict_types=1);
|
|||
|
|
/**
|
|||
|
|
* This file is part of the SplendidBear Websites' projects.
|
|||
|
|
*
|
|||
|
|
* Copyright (c) 2026 @ www.splendidbear.org
|
|||
|
|
*
|
|||
|
|
* For the full copyright and license information, please view the LICENSE
|
|||
|
|
* file that was distributed with this source code.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
namespace App\Tests\Controller;
|
|||
|
|
|
|||
|
|
use App\Tests\Factory\UserFactory;
|
|||
|
|
use App\Tests\WebTestCase;
|
|||
|
|
|
|||
|
|
class MyControllerTest extends WebTestCase
|
|||
|
|
{
|
|||
|
|
public function testExample(): void
|
|||
|
|
{
|
|||
|
|
$user = UserFactory::createOne();
|
|||
|
|
$client = static::createClient();
|
|||
|
|
|
|||
|
|
$client->request('GET', '/profile');
|
|||
|
|
|
|||
|
|
self::assertResponseRedirects('/login');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Important**: Always extend `App\Tests\WebTestCase`, not `Symfony\Bundle\FrameworkBundle\Test\WebTestCase`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Directory Structure
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
tests/
|
|||
|
|
├── Controller/ # HTTP endpoint tests
|
|||
|
|
├── Dto/ # Data Transfer Object tests
|
|||
|
|
├── Entity/ # Entity logic tests
|
|||
|
|
├── Service/ # Service layer tests
|
|||
|
|
├── Integration/ # Integration tests
|
|||
|
|
├── Factory/ # Foundry factories
|
|||
|
|
├── WebTestCase.php # Base test class
|
|||
|
|
└── bootstrap.php # PHPUnit bootstrap
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## MineSeeker-Specific Patterns
|
|||
|
|
|
|||
|
|
### Testing Game Flow
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
public function testRedPlayerWinsGame(): void
|
|||
|
|
{
|
|||
|
|
$game = PlayedGameFactory::new()
|
|||
|
|
->withRegisteredPlayers()
|
|||
|
|
->create();
|
|||
|
|
|
|||
|
|
// Create steps to simulate gameplay
|
|||
|
|
for ($i = 0; $i < 26; $i++) {
|
|||
|
|
StepFactory::new()
|
|||
|
|
->forPlayer('red')
|
|||
|
|
->mine()
|
|||
|
|
->create(['playedGame' => $game]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
self::assertEquals(26, $game->redPoints);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Testing Battle Sharing
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
public function testBattleSharePageReturnsValidGame(): void
|
|||
|
|
{
|
|||
|
|
$game = PlayedGameFactory::new()
|
|||
|
|
->withRegisteredPlayers()
|
|||
|
|
->redWins()
|
|||
|
|
->create();
|
|||
|
|
|
|||
|
|
$client = static::createClient();
|
|||
|
|
$client->request('GET', '/battle/' . $game->uuid);
|
|||
|
|
|
|||
|
|
self::assertResponseIsSuccessful();
|
|||
|
|
self::assertSelectorTextContains('h1', 'Battle Report');
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Testing Authentication
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
public function testProfileRequiresAuthentication(): void
|
|||
|
|
{
|
|||
|
|
$client = static::createClient();
|
|||
|
|
$client->request('GET', '/profile');
|
|||
|
|
|
|||
|
|
self::assertResponseRedirects('/login');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function testAuthenticatedUserCanAccessProfile(): void
|
|||
|
|
{
|
|||
|
|
$user = UserFactory::createOne();
|
|||
|
|
$client = static::createClient();
|
|||
|
|
$client->loginUser($user->_real());
|
|||
|
|
|
|||
|
|
$client->request('GET', '/profile');
|
|||
|
|
|
|||
|
|
self::assertResponseIsSuccessful();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Troubleshooting
|
|||
|
|
|
|||
|
|
### Tests interfering with each other?
|
|||
|
|
|
|||
|
|
Ensure you extend `App\Tests\WebTestCase`, not the base Symfony class.
|
|||
|
|
|
|||
|
|
### Schema out of sync?
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
make test-db-reset
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Memory limit errors?
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
php -d memory_limit=1024M vendor/bin/phpunit
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Or increase in `phpunit.dist.xml`:
|
|||
|
|
```xml
|
|||
|
|
<ini name="memory_limit" value="1024M"/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Test database doesn't exist?
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
make test-db-setup
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## External Resources
|
|||
|
|
|
|||
|
|
- **[Symfony Testing](https://symfony.com/doc/current/testing.html)** - Symfony testing guide
|
|||
|
|
- **[Zenstruck Foundry](https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html)** - Factory documentation
|
|||
|
|
- **[DAMA Doctrine Test Bundle](https://github.com/dmaicher/doctrine-test-bundle)** - Transaction isolation
|
|||
|
|
- **[PHPUnit](https://docs.phpunit.de/)** - PHPUnit documentation
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Modern PHPUnit Attributes
|
|||
|
|
|
|||
|
|
MineSeeker tests use modern PHP 8 attributes instead of method name prefixes:
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
use PHPUnit\Framework\Attributes\Test;
|
|||
|
|
use PHPUnit\Framework\Attributes\TestDox;
|
|||
|
|
|
|||
|
|
#[TestDox('Security Controller')]
|
|||
|
|
class SecurityControllerTest extends WebTestCase
|
|||
|
|
{
|
|||
|
|
#[Test]
|
|||
|
|
#[TestDox('Login page loads successfully with form fields')]
|
|||
|
|
public function loginPageLoadsSuccessfully(): void
|
|||
|
|
{
|
|||
|
|
// Test implementation
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Benefits:**
|
|||
|
|
- ✅ More readable method names (no `test` prefix required)
|
|||
|
|
- ✅ Self-documenting with `TestDox` descriptions
|
|||
|
|
- ✅ Better IDE support and refactoring
|
|||
|
|
- ✅ Cleaner `--testdox` output
|
|||
|
|
|
|||
|
|
**Run with documentation:**
|
|||
|
|
```bash
|
|||
|
|
vendor/bin/phpunit --testdox
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Output:
|
|||
|
|
```
|
|||
|
|
Security Controller
|
|||
|
|
✔ Login page loads successfully with form fields
|
|||
|
|
✔ Login page has links to register and forgot password
|
|||
|
|
✔ Register page loads successfully with form
|
|||
|
|
```
|