Why Regular Unit Tests Don't Save You, and What is E2E?
\n\nImagine: you've written perfect code. Unit tests are green, coverage is 90%, the architecture is a gem. You roll out a release... and users complain that the "Pay" button doesn't work, even though all records in the database are correct. Sound familiar?
\n\nThe problem is that unit tests check isolated pieces of code. They don't see how your PHP method interacts with the JavaScript validator on the frontend, how the session is passed via cookies, or what happens when Redis suddenly goes down. This is where E2E (End-to-End) testing comes into play.
\n\nE2E tests simulate real user behavior: clicking buttons, filling out forms, checking redirects and API responses. In the PHP world, this is especially relevant because a typical application is a complex tangle of Laravel/Symfony, databases, queues, and external services. Without E2E, you risk missing bugs that will ruin the user experience.
\n\nTools for E2E Testing of PHP Projects
\n\nIn the PHP ecosystem, there are two main players. I won't keep you in suspense — here they are:
\n\n- \n
- Codeception — the most popular framework that combines unit, functional, and E2E tests. It supports Selenium WebDriver, PHPBrowser, and REST API out of the box. \n
- PHPUnit + Selenium WebDriver — a classic combination where PHPUnit handles execution and Selenium handles browser emulation. Requires more manual configuration but gives you full control. \n
Personally, I recommend Codeception for getting started: it works well with popular CMS (WordPress, Drupal) and frameworks (Laravel, Yii2), and configuration takes 10 minutes.
\n\nInstalling Codeception for E2E
\n\nCreate a composer.json file in the project root (if it doesn't exist) and run:
composer require --dev codeception/codeception codeception/module-webdriver codeception/module-phpbrowser\n\nThen initialize the framework:
\n\nvendor/bin/codecept init\n\nYou will be asked what type of tests to create. Choose Acceptance (E2E). After that, the tests/acceptance folder and the codeception.yml config file will appear.
Writing Your First E2E Test: User Registration
\n\nThe most common scenario is registration. Let's check that the form works correctly and that the user sees a greeting after submission.
\n\nConfiguration for Working with a Browser
\n\nOpen tests/acceptance.suite.yml and configure the WebDriver module (for Chrome):
actor: AcceptanceTester\nmodules:\n enabled:\n - WebDriver:\n url: 'http://localhost:8000' # URL of your local server\n browser: chrome\n window_size: 1920x1080\n capabilities:\n chromeOptions:\n args: ["--headless", "--no-sandbox"] # headless for CI\n - Db:\n dsn: 'mysql:host=localhost;dbname=testdb'\n user: 'root'\n password: ''\n populate: true\n cleanup: true\n\n\nImportant: don't forget to start the Selenium Server (or ChromeDriver) in a separate terminal:
\n\njava -jar selenium-server-standalone.jar\n\nTest Code
\n\nCreate a file tests/acceptance/RegistrationCest.php:
<?php\nuse Codeception\\Util\\Locator;\n\nclass RegistrationCest\n{\n public function _before(AcceptanceTester $I)\n {\n $I->amOnPage('/register');\n }\n\n public function testSuccessfulRegistration(AcceptanceTester $I)\n {\n // 1. Fill out the form\n $I->fillField('input[name="name"]', 'Ivan Petrov');\n $I->fillField('input[name="email"]', 'ivan@example.com');\n $I->fillField('input[name="password"]', 'StrongPass123!');\n