Testing
dde uses PHPUnit for all tests. Tests are organized into unit tests and E2E tests, with strict separation.
Test Structure
Section titled “Test Structure”Tests mirror the src/ directory structure:
tests/ Unit/ Manager/ ImageManagerTest.php ConfigManagerTest.php ... Doctor/ Check/ BinaryPathCheckTest.php ... ... E2E/ ...Running Tests
Section titled “Running Tests”# All tests except E2E (default for CI)make test
# Unit tests onlymake test-unit
# E2E tests (requires Docker)make test-e2e
# Tests with coverage reportmake test-coverageTest Categories
Section titled “Test Categories”Unit Tests
Section titled “Unit Tests”Unit tests verify individual classes in isolation. They mock dependencies and do not require Docker or any external services.
Every new class should have a corresponding unit test covering at minimum:
- The happy path (expected behavior with valid inputs)
- The most important error cases (invalid inputs, edge cases)
E2E Tests
Section titled “E2E Tests”E2E tests require a running Docker daemon and are tagged with:
#[Group('e2e')]These tests are excluded from the standard test suite and CI. Run them explicitly with make test-e2e.
Full QA Suite
Section titled “Full QA Suite”The make qa target runs the complete quality assurance pipeline:
- ECS — coding standard check and auto-fix
- PHPStan — static analysis at level 8
- Rector — automated refactoring (dry-run in QA mode)
- Tests — PHPUnit excluding E2E
All four steps must pass before a commit is considered ready.
Writing Tests
Section titled “Writing Tests”Example: Unit Test
Section titled “Example: Unit Test”<?php
declare(strict_types=1);
namespace App\Tests\Unit\Manager;
use App\Manager\ImageManager;use App\Manager\DockerManager;use App\Model\UserContext;use PHPUnit\Framework\TestCase;
final class ImageManagerTest extends TestCase{ public function testHasLabelReturnsFalseForMissingImage(): void { $dockerManager = $this->createMock(DockerManager::class); $dockerManager->method('inspect') ->willThrowException(new \RuntimeException('not found'));
$userContext = new UserContext(uid: 1000, gid: 1000); $manager = new ImageManager($dockerManager, $userContext);
self::assertFalse($manager->hasLabel('nonexistent:latest', 'dde.configured')); }}Conventions
Section titled “Conventions”- Test classes are
final - Test methods use
testprefix (not@testannotation) - Use
self::assert*()instead of$this->assert*() - Mock external dependencies (Docker, filesystem, processes)
- Each test method tests one behavior