Test a web application
Test an application through its URL, whether it is a Contao project or uses another framework or language. The tooling runs real browsers and provides PHPUnit assertions.
Choose how to serve the application: connect to a server already running at a URL, or let the tests start and stop a local server. The tooling can serve a PHP project directly or run your application's own startup command. Your project still prepares dependencies, builds and test data.
For Contao backend tests, add the Contao-specific checks after completing this guide.
Prepare your application
Use a test environment whose homepage contains an h1. Install and build the application using your project's normal commands. For a server that is already running, check that you can open its URL in a browser. The URL-based example uses http://localhost:8080, so replace that address with yours.
Install the supported PHP, Composer and Node.js versions on the machine that runs PHPUnit. PHP is needed for the tests even if the application uses another language. Docker and MySQL are needed only if your application uses them.
Install the tooling
Run from the repository where you want to keep the tests. This can be your application repository or a separate test repository. If it has no composer.json, run composer init first.
composer require --dev contao/e2e-testing
vendor/bin/playwright-install --browsers
Add .contao-e2e/ to that repository's .gitignore. It contains test caches and traces.
Add your first test
Create tests/E2e/HomepageTest.php:
<?php
declare(strict_types=1);
use Contao\E2eTesting\Application\AbstractApplicationTestCase;
use Contao\E2eTesting\Application\ApplicationConfig;
use Contao\E2eTesting\Application\ApplicationConfigInterface;
final class HomepageTest extends AbstractApplicationTestCase
{
protected static function createApplicationConfig(): ApplicationConfigInterface
{
return ApplicationConfig::create(getenv('E2E_BASE_URL') ?: 'http://localhost:8080');
}
public function testHomepage(): void
{
self::application()->createBrowser()->visit('/');
$this->assertSelectorExists('h1');
}
}
Start a local server instead
For a PHP project with a public/ document root, add this import at the top of the test file:
use Contao\E2eTesting\Application\LocalApplicationConfig;
Replace the return statement in createApplicationConfig() with:
return LocalApplicationConfig::php(dirname(__DIR__, 2));
This example assumes the tests are inside the application repository. If you keep tests in a separate repository or serve an application below the monorepo root, pass that application's directory instead.
The tests now start PHP's built-in server on a free loopback port and stop it after the test class. You do not need to start a server manually or set E2E_BASE_URL. For another document root or a Node.js, Python or other startup command, see local application servers.
Configure PHPUnit
Create phpunit.xml.dist in the test repository root, or add this suite to your existing configuration:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" cacheDirectory=".contao-e2e/cache/phpunit">
<testsuites>
<testsuite name="e2e">
<directory>tests/E2e</directory>
</testsuite>
</testsuites>
</phpunit>
Run the test
For tests that start their own local server, run from the test repository root:
vendor/bin/phpunit --testsuite=e2e
For tests that connect to an existing server, keep that server running and supply its URL:
E2E_BASE_URL=http://localhost:8080 vendor/bin/phpunit --testsuite=e2e
PowerShell:
$env:E2E_BASE_URL = 'http://localhost:8080'
vendor/bin/phpunit --testsuite=e2e
PHPUnit should report a passing test. Replace the h1 check with an assertion that matters for your application.
A URL subdirectory is supported. For example, with E2E_BASE_URL=https://example.test/app, visit('/login') opens /app/login. Use an absolute HTTP or HTTPS URL without a query or fragment.
Use this in a monorepo
The same setup works in a non-Contao monorepo. Keep the Composer test harness at the repository root for shared tests, or in an application directory for tests owned by that application. Run the installation and PHPUnit commands from that harness directory.
Your application's build or package manager handles local package dependencies. Point ApplicationConfig at an existing server URL, or give LocalApplicationConfig the application directory and its startup command. You do not need MonorepoProject for these tests.
If the monorepo contains several applications, give each application's test class its own configuration. Local servers get independent ports. For existing servers, read a separate URL variable such as SHOP_E2E_BASE_URL or ADMIN_E2E_BASE_URL in each class.
Keep tests independent
The tooling closes browser contexts between tests, clearing their cookies and browser storage. It does not reset your application's database, uploads or other server-side state. Use your project's test setup to restore those before tests that modify them.
See custom PHPUnit integration for reset hooks. Continue with browser assertions, debugging traces or CI setup.
Improve this page