Quick reference for running the test suites. For full setup documentation — bootstrap wiring, suite configuration, writing new tests — see Testing with Codeception.
Suite selection: Use Functional for API endpoint testing (emulates HTTP through the Symfony kernel, no browser needed). Use Acceptance for pages that require JavaScript — it drives a real Firefox browser via Selenium, which is already included in docker-compose.yml.
Before running any tests make sure:
composer install and npm installAPP_ENV=test php bin/console doctrine:schema:createconfig/constants.php exists and is valid# Run all tests
php bin/phpunit
# Run a specific directory
php bin/phpunit tests/System/Core/Cache/
php bin/phpunit tests/System/Classes/
php bin/phpunit tests/Functions/
# Run a specific file
php bin/phpunit tests/System/Core/Cache/RedisCacheAdapterTest.php
# Run a specific test method
php bin/phpunit --filter testDeleteByKeyPattern tests/System/Core/Cache/RedisCacheAdapterTest.php
# Run with coverage (requires Xdebug or PCOV)
php bin/phpunit --coverage-html var/coverage
# Run with verbose output
php bin/phpunit --verbose
# Stop on first failure
php bin/phpunit --stop-on-failure
Copy phpunit.xml.dist to phpunit.xml to customise your local run without affecting the committed configuration:
cp phpunit.xml.dist phpunit.xml
# Run all suites
php vendor/bin/codecept run
# Run a specific suite
php vendor/bin/codecept run Unit
php vendor/bin/codecept run Functional
php vendor/bin/codecept run Acceptance
# Run a specific test file
php vendor/bin/codecept run Unit ExampleUnitCest
# Run a specific test method
php vendor/bin/codecept run Unit ExampleUnitCest:testSomething
# Run with coverage
php vendor/bin/codecept run --coverage
php vendor/bin/codecept run --coverage-html
# Run only previously failed tests
php vendor/bin/codecept run -g failed
# Run with steps output
php vendor/bin/codecept run --steps
# Run with verbose debug output
php vendor/bin/codecept run --debug
The RunFailed Codeception extension tags failed tests automatically — after fixing a failure, re-run only failed tests with -g failed rather than the full suite.
# Run all Jest tests
npm run test:unit
# Watch mode — re-runs on file change
npm run test:unit -- --watch
# Run a specific test file
npm run test:unit -- src/Tests/Unit/example.js
# With coverage
npm run test:unit -- --coverage
k6 must be installed separately — it is not in the npm or Composer dependency tree. See k6.io for installation instructions.
The application must be running before executing performance tests:
./run.sh
# Basic run — single virtual user
k6 run src/Tests/Performance/load.test.js
# With virtual users and duration
k6 run --vus 10 --duration 30s src/Tests/Performance/load.test.js
# Ramp up, sustain, ramp down
k6 run --stage 10s:10,30s:50,10s:0 src/Tests/Performance/load.test.js
# With summary output to file
k6 run --out json=results.json src/Tests/Performance/load.test.js
The framework ships with tests for its own internal components. These run as part of the project test suite via PHPUnit.
Tests for all three cache adapters covering the full PSR-16 interface — get, set, delete, has, getMultiple, setMultiple, deleteMultiple, deleteByKeyPattern, and clear:
tests/System/Core/Cache/
├── RedisCacheAdapterTest.php
├── APCuCacheAdapterTest.php
└── MemcachedCacheAdapterTest.php
APCu tests are skipped automatically when APCu is not available or not enabled:
protected function setUp(): void
{
if ( $this->isAPCuEnabled === false )
$this->markTestSkipped( 'APCu is not enabled' );
}
Each test cleans up after itself via tearDown() — aca()->clear(), rca()->clear(), mca()->clear() — so tests don't bleed state between methods.
Tests for all three middleware composition types — MiddlewareAll, MiddlewareAny, MiddlewareCustom — covering:
tests/System/Classes/MiddlewareChecks/
└── MiddlewaresExecutorTest.php
These tests require the kernel instance from $GLOBALS['kernel'] set in bootstrap.php and use four local middleware stubs defined at the top of the file:
final class MiddlewareTrue extends Middleware
{ public function result(): bool { return true; } }
final class MiddlewareFalse extends Middleware
{ public function result(): bool { return false; } }
final class MiddlewareJsonResponse extends Middleware
{ public function result(): JsonResponse { return new JsonResponse( [ 'test' => 'test' ] ); } }
final class MiddlewareRedirectResponse extends Middleware
{ public function result(): RedirectResponse { return $this->redirectTo( 'welcome_page' ); } }
Router $currentRoute must be set manually for tests that check redirect vs JSON response behaviour since the middleware checks the current URL to decide which response type to return:
// API route — middleware blocks return JsonResponse
Router::$currentRoute = (object)[ 'url' => '/api/middleware_testing' ];
// Page route — middleware blocks return RedirectResponse
Router::$currentRoute = (object)[ 'url' => '/middleware_testing' ];
Tests for the input() view helper function covering every parameter — id, required, placeholder, default value, type, number step, minMax, classes, styles, custom attributes — including all validation exceptions:
tests/Functions/
└── InputFunctionTest.php
These are pure unit tests with no external dependencies — no Redis, no database, no kernel needed. They test string output directly:
public function testId(): void
{
$actual = input( 'input_name', id: 'number' );
$expected = "<input type='text' required id='number' name='input_name' minlength='1' maxlength='255'>";
$this->assertEquals( $expected, $actual );
}
php bin/phpunit tests/System/Core/Cache/
php bin/phpunit tests/System/Classes/MiddlewareChecks/
php bin/phpunit tests/Functions/
php bin/phpunit tests/System/ tests/Functions/
php bin/phpunit tests/Unit/ tests/Functional/
A typical CI sequence that covers the full test stack:
# 1. Install dependencies
composer install --no-interaction
npm install
# 2. Set up test environment
cp .env.test .env.local
APP_ENV=test php bin/console doctrine:schema:create
APP_ENV=test php bin/console doctrine:fixtures:custom-loader -f
# 3. Clear all cache
php bin/console app:cache:clear
php bin/console symfony:cache:clear
# 4. Run PHPUnit
php bin/phpunit --stop-on-failure
# 5. Run Codeception unit and functional suites
php vendor/bin/codecept run Unit
php vendor/bin/codecept run Functional
# 6. Run Jest
npm run test:unit
# 7. (Optional) Start app and run acceptance tests
./run.sh &
sleep 3
php vendor/bin/codecept run Acceptance
Acceptance tests are typically separated from the main CI pipeline or run on a schedule rather than on every commit — they're slow and require a browser driver.
Running tests without Redis — RedisCacheAdapterTest and any test that uses ca(), rca(), or em() requires Redis to be running. A missing Redis connection fails immediately with a connection refused error. Start Docker services before running tests:
docker-compose up -d redis postgres
Running acceptance tests without the dev server or Selenium — acceptance tests connect to https://host.docker.internal:7000/ via WebDriver and require Selenium running on port 4444. If either is missing, all acceptance tests fail with a connection error. Start both with docker-compose up -d and ./run.sh before running the acceptance suite.
Not copying phpunit.xml.dist — phpunit.xml is gitignored. On a fresh clone, php bin/phpunit uses phpunit.xml.dist. If you customise your local run (different filter, different coverage output path), copy it to phpunit.xml first so your changes don't affect other developers.
Skipping tearDown() in new tests — tests that write to Redis or the database without cleaning up in tearDown() cause unpredictable failures in subsequent test methods. The order that PHPUnit runs test methods within a class is not guaranteed. Always clean up.