diff --git a/src/guide/index.md b/src/guide/index.md index 647581e4..9fb688f2 100644 --- a/src/guide/index.md +++ b/src/guide/index.md @@ -117,12 +117,12 @@ We release this guide under the [Terms of Yii Documentation](https://www.yiifram ## Testing -- [Testing overview](testing/overview.md) TODO -- [Testing environment setup](testing/environment-setup.md) TODO -- [Unit tests](testing/unit.md) TODO -- [Functional tests](testing/functional.md) TODO -- [Acceptance tests](testing/acceptance.md) TODO -- [Fixtures](testing/fixtures.md) TODO +- [Testing overview](testing/overview.md) +- [Testing environment setup](testing/environment-setup.md) +- [Unit tests](testing/unit.md) +- [Functional tests](testing/functional.md) +- [End-to-end tests](testing/end-to-end.md) +- [Static analysis and mutation testing](testing/quality-tools.md) ## Special topics diff --git a/src/guide/testing/end-to-end.md b/src/guide/testing/end-to-end.md new file mode 100644 index 00000000..774981d2 --- /dev/null +++ b/src/guide/testing/end-to-end.md @@ -0,0 +1,127 @@ +# End-to-end tests + +The Yii application template uses the `Web` Codeception suite for tests that go through an HTTP server. These tests live +in `tests/Web` and use `App\Tests\Support\WebTester`. + +Use web tests for user-visible HTTP behavior: pages, links, forms, redirects, cookies, and error pages. + +## Web suite + +The template configures `tests/Web.suite.yml` like this: + +```yaml +actor: WebTester +extensions: + enabled: + - Codeception\Extension\RunProcess: + 0: composer serve + sleep: 3 +modules: + enabled: + - PhpBrowser: + url: http://127.0.0.1:8080 +``` + +`RunProcess` starts the application with the Composer `serve` script. `PhpBrowser` sends HTTP requests to the server. + +## Test a page + +The template includes `tests/Web/HomePageCest.php`: + +```php +wantTo('home page works.'); + $I->amOnPage('/'); + $I->expectTo('see page home.'); + $I->see('Hello!'); + } +} +``` + +Run the web suite locally: + +```shell +APP_ENV=test vendor/bin/codecept run Web +``` + +Run only this test: + +```shell +APP_ENV=test vendor/bin/codecept run Web HomePageCest +``` + +With Docker: + +```shell +make test Web +``` + +## Test links and error pages + +Use the same actor methods for navigation and response assertions: + +```php +wantTo('see 404 page.'); + $I->amOnPage('/non-existent-page'); + $I->canSeeResponseCodeIs(404); + $I->see('404'); + $I->see('The page /non-existent-page not found.'); + } +} +``` + +## Smoke test with curl + +For a deployment smoke test, start the application and check one URL: + +```shell +APP_ENV=test ./yii serve --port=8080 +``` + +In another terminal: + +```shell +curl -fsS http://127.0.0.1:8080/ > /tmp/home.html +grep -q "Hello!" /tmp/home.html +``` + +## Reset state + +Web tests use real infrastructure, so reset state before each scenario: + +- Load only the records required by the scenario. +- Clear session and cookie storage. +- Clear generated files and outgoing messages. +- Stop background workers, or make their effects deterministic. + +If the scenario changes a database, reset it in the Cest `_before()` hook or in a project-specific helper: + +```php +public function _before(WebTester $I): void +{ + // Reset database tables, files, queues, and outgoing messages. +} +``` diff --git a/src/guide/testing/environment-setup.md b/src/guide/testing/environment-setup.md new file mode 100644 index 00000000..9a0d5d6f --- /dev/null +++ b/src/guide/testing/environment-setup.md @@ -0,0 +1,188 @@ +# Testing environment setup + +The Yii application template has the test environment configured. Use this page when creating a new project from the +template, restoring removed test files, or adding a new suite. + +## Composer configuration + +The template requires Codeception, PHPUnit, and the modules used by the suites: + +```json +{ + "require-dev": { + "codeception/codeception": "^5.3", + "codeception/module-asserts": "^3.2", + "codeception/module-cli": "^2.0", + "codeception/module-phpbrowser": "^3.0", + "phpunit/phpunit": "^11.5" + }, + "autoload-dev": { + "psr-4": { + "App\\Tests\\": "tests" + } + }, + "scripts": { + "test": "codecept run" + } +} +``` + +If these entries are already in your project, keep the installed versions from `composer.lock`. + +## Main Codeception file + +The root `codeception.yml` defines the test namespace, paths, bootstrap file, and coverage source: + +```yaml +namespace: App\Tests +support_namespace: Support +bootstrap: bootstrap.php + +settings: + shuffle: true + colors: true + +paths: + tests: tests + output: tests/_output + data: tests/Support/Data + support: tests/Support + +coverage: + enabled: true + show_uncovered: true + show_only_summary: true + include: + - src/* + - public/index.php + - yii + +extensions: + enabled: + - Codeception\Extension\RunFailed +``` + +`tests/bootstrap.php` prepares the application environment for every suite: + +```php +runAndGetResponse($request); + $body = $response->getBody(); + if ($body->isSeekable()) { + $body->rewind(); + } + + return $response; + } +} +``` + +## Test a page + +The template includes `tests/Functional/HomePageCest.php`: + +```php +sendRequest( + new ServerRequest(uri: '/'), + ); + + assertSame(200, $response->getStatusCode()); + assertStringContainsString( + 'Don\'t forget to check the guide', + $response->getBody()->getContents(), + ); + } +} +``` + +Run it: + +```shell +APP_ENV=test vendor/bin/codecept run Functional +APP_ENV=test vendor/bin/codecept run Functional HomePageCest +``` + +## Send request data + +Use PSR-7 methods to model the request: + +```php +$request = (new ServerRequest(uri: '/contact', method: 'POST')) + ->withHeader('Content-Type', 'application/x-www-form-urlencoded') + ->withParsedBody([ + 'ContactForm' => [ + 'name' => 'Sam', + 'email' => 'sam@example.test', + 'body' => 'Hello.', + ], + ]); + +$response = $tester->sendRequest($request); + +assertSame(302, $response->getStatusCode()); +assertSame('/contact/sent', $response->getHeaderLine('Location')); +``` + +For JSON APIs, write the JSON body into a PSR-7 stream and set `Content-Type: application/json`. + +## Reset state + +Functional tests often touch runtime files, sessions, cache, and a database. Reset changed state in the Cest `_before()` +hook or in a project helper: + +```php +public function _before(FunctionalTester $tester): void +{ + // Reset database tables, cache pools, queues, and outgoing messages. +} +``` + +Keep pure domain rules in unit tests. Put request and response behavior in functional tests. diff --git a/src/guide/testing/overview.md b/src/guide/testing/overview.md new file mode 100644 index 00000000..dcb654a5 --- /dev/null +++ b/src/guide/testing/overview.md @@ -0,0 +1,98 @@ +# Testing + +The Yii application template includes a ready test setup. It uses Codeception with PHPUnit assertions and has four +suites: + +- `Unit` for isolated PHP classes. +- `Functional` for application code called in the same PHP process. +- `Web` for requests sent through an HTTP server. +- `Console` for console commands. + +The main files are: + +- `codeception.yml`. +- `tests/bootstrap.php`. +- `tests/Unit.suite.yml`. +- `tests/Functional.suite.yml`. +- `tests/Web.suite.yml`. +- `tests/Console.suite.yml`. +- `tests/Support/*Tester.php`. + +## Run tests locally + +Build actor classes after installing dependencies or changing a suite: + +```shell +vendor/bin/codecept build +``` + +Run all tests: + +```shell +APP_ENV=test vendor/bin/codecept run +``` + +The template also defines a Composer script: + +```shell +APP_ENV=test composer test +``` + +Run one suite: + +```shell +APP_ENV=test vendor/bin/codecept run Unit +APP_ENV=test vendor/bin/codecept run Functional +APP_ENV=test vendor/bin/codecept run Web +APP_ENV=test vendor/bin/codecept run Console +``` + +Run one test class or method: + +```shell +APP_ENV=test vendor/bin/codecept run Functional HomePageCest +APP_ENV=test vendor/bin/codecept run Functional HomePageCest:base +``` + +`APP_ENV=test` is required for local commands because `tests/bootstrap.php` prepares the application environment before +tests run. + +## Run tests in Docker + +Build actor classes: + +```shell +make codecept build +``` + +Run all tests: + +```shell +make test +``` + +Run one suite: + +```shell +make test Unit +make test Functional +make test Web +make test Console +``` + +The Docker test environment reads `docker/test/.env`, where `APP_ENV=test` is already set. + +## Choose a suite + +Start with unit tests for code that has no framework boundary: value objects, validators, domain services, and +transformers. + +Use functional tests when code needs Yii configuration, dependency injection, routing, middleware, request handling, or +template rendering. + +Use web tests for behavior that must go through an HTTP server: status codes, links, redirects, cookies, and rendered +pages as seen by a client. + +Use console tests for commands in `src/Console`. + +Run static analysis in CI and before changing shared contracts. diff --git a/src/guide/testing/quality-tools.md b/src/guide/testing/quality-tools.md new file mode 100644 index 00000000..6fd08618 --- /dev/null +++ b/src/guide/testing/quality-tools.md @@ -0,0 +1,106 @@ +# Static analysis and code quality + +The Yii application template includes static analysis and code quality tools alongside the test suite. + +## Psalm + +Run Psalm locally: + +```shell +vendor/bin/psalm +``` + +Run Psalm in Docker: + +```shell +make psalm +``` + +The template stores Psalm configuration in `psalm.xml`. The GitHub Actions workflow runs Psalm for supported PHP +versions. + +When Psalm reports an issue, fix the code or add a precise type annotation. Keep suppressions narrow and local to the +line or method that needs them. + +## Composer Dependency Analyser + +Composer Dependency Analyser checks that `composer.json` matches the classes used by the application. + +Run it locally: + +```shell +vendor/bin/composer-dependency-analyser --config=composer-dependency-analyser.php +``` + +Run it in Docker: + +```shell +make composer-dependency-analyser +``` + +Use this check after adding or removing package usage in `src`, `config`, or `tests`. + +## PHP CS Fixer + +PHP CS Fixer applies the project coding style from `.php-cs-fixer.php`. + +Run it locally: + +```shell +vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff +``` + +Run it in Docker: + +```shell +make cs-fix +``` + +Commit formatting changes together with the code that needs them. + +## Rector + +Rector applies configured code upgrades and refactorings from `rector.php`. + +Preview changes locally: + +```shell +vendor/bin/rector --dry-run +``` + +Apply changes locally: + +```shell +vendor/bin/rector +``` + +Run it in Docker: + +```shell +make rector +``` + +Review Rector changes before committing them. Automated refactoring can change behavior when custom rules or broad paths +are configured. + +## Pull request checks + +A practical local check before opening a pull request is: + +```shell +APP_ENV=test vendor/bin/codecept build +APP_ENV=test vendor/bin/codecept run +vendor/bin/psalm +vendor/bin/composer-dependency-analyser --config=composer-dependency-analyser.php +vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff +``` + +With Docker: + +```shell +make codecept build +make test +make psalm +make composer-dependency-analyser +make cs-fix +``` diff --git a/src/guide/testing/unit.md b/src/guide/testing/unit.md new file mode 100644 index 00000000..f9cb8450 --- /dev/null +++ b/src/guide/testing/unit.md @@ -0,0 +1,211 @@ +# Unit tests + +Unit tests check a small piece of PHP code directly. In the Yii application template they live in `tests/Unit` and run +through the `Unit` Codeception suite. + +The template includes `tests/Unit/EnvironmentTest.php`: + +```php +generate('Hello, Yii3!'); + + assertSame('hello-yii3', $slug); + } +} +``` + +Run it: + +```shell +APP_ENV=test vendor/bin/codecept run Unit SlugGeneratorTest +``` + +## Test services with dependencies + +Pass dependencies through the constructor and use test doubles for I/O. + +Create `src/Clock/ClockInterface.php`: + +```php +clock->now()); + } +} +``` + +Create `tests/Unit/PublishPostTest.php`: + +```php +publish('Testing Yii'); + + assertSame('Testing Yii', $post->title); + assertSame('2026-05-07 10:00:00', $post->publishedAt->format('Y-m-d H:i:s')); + } +} +``` + +Run it: + +```shell +APP_ENV=test vendor/bin/codecept run Unit PublishPostTest +``` + +Keep unit tests focused on one class or one small collaboration. Use functional tests when the behavior depends on Yii +configuration, dependency injection, routing, or middleware.