# Oro Functional Testing

> Use when writing or debugging PHPUnit functional tests for Oro Commerce 6.1 — anything extending WebTestCase: controllers, REST API, console commands (runCommand), datagrids (requestGrid), ACL/403 flows, alice fixtures. Triggers on initClient, loadFixtures, getReference, generateBasicAuthHeader, generateApiAuthHeader, getJsonResponseContent; on @dbIsolationPerTest, @outputBuffering, @depends; on test-env setup (oro:install --env=test, install_options, .env-app.test.local, ORO_DB_DSN, --user-email ignored); on state bleed between methods or fixtures, EM clearing, InitialFixtureInterface, unit-vs-functional splits.

- Skill: `netresearch/oro-functional-testing` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add netresearch/oro-functional-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/netresearch/oro-functional-testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: netresearch (https://skillmd.com/u/netresearch)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/netresearch/oro-functional-testing

---


# OroCommerce v6.1 Functional Testing (PHPUnit)

Functional tests exercise real controllers, services, commands, APIs and ACL rules against a real database. `WebTestCase` boots the kernel, manages transactional isolation, loads fixtures deterministically and layers HTTP/grid/JSON helpers over `BrowserKit`.

## Canonical WebTestCase — client, fixtures, isolation, assertion

```php
/**
 * @dbIsolationPerTest
 * @outputBuffering enabled
 */
class DocumentControllerTest extends WebTestCase
{
    protected function setUp(): void
    {
        $this->initClient([], $this->generateBasicAuthHeader());   // admin@example.com / admin
        $this->loadFixtures([LoadDocumentData::class]);
    }

    public function testView(): void
    {
        $document = $this->getReference('document.first');
        $this->client->request('GET', $this->getUrl('acme_demo_document_view', ['id' => $document->getId()]));
        $this->assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200);
    }
}
```

`loadFixtures()` runs once per test case, resolving dependencies via `DependentFixtureInterface`; `getReference()` retrieves what a fixture registered. EM clearing: `references/fixtures.md`.

## WebTestCase Helpers

`generateBasicAuthHeader()` for HTML forms, `generateApiAuthHeader($user)` for the REST API — there the framework issues the token, so no password. Also `loadFixtures`, `getReference`, `getContainer`, `runCommand`, `requestGrid`, `getJsonResponseContent`, `getUrl` and the two status-code assertions. Signatures: `references/webtestcase-helpers.md`.

## Isolation Annotations

- `@dbIsolationPerTest` — a transaction per **method**, rolled back at the end. Without it the class shares one transaction and state bleeds.
- `@outputBuffering enabled` — required on `WebTestCase` subclasses; strict output mode otherwise fails on any echo during request handling.
- `@depends` — inherits fixtures **and state**. Only for sequential scenarios where isolation would defeat the test.

## API, command, grid

Same kernel, different helper and auth header:

```php
$this->initClient([], $this->generateApiAuthHeader('admin@example.com'));   // REST
$result = $this->getJsonResponseContent($this->client->getResponse(), 200);
$output = $this->runCommand('oro:search:reindex', ['--class' => 'OroUserBundle:User']);
$response = $this->client->requestGrid('users-grid', ['users-grid[_filter][username][value]' => 'admin']);
```

`runCommand` needs no auth header, but still `initClient()`. Filter brackets: `references/grid-testing.md`; 403 flows: `references/acl-patterns.md`.

## Test Environment Installation

`php bin/console oro:install --env=test`. **CLI flags such as `--user-email=…` are silently ignored there**, so install parameters must come from `oro_test_framework.install_options` in `config/config.yml` (`references/install-options.md`). `ORO_DB_DSN` and `ORO_MAILER_DSN` live in `.env-app.test.local`.

## Running Tests

```bash
docker compose run --rm toolbox bin/phpunit -c ./ --testsuite=functional
docker compose run --rm toolbox bin/phpunit -c ./ --testsuite=unit
```

**Never both in one invocation** — mocks from the unit suite break kernel boot and service resolution in the functional one, surfacing as service-not-found errors in unrelated tests.

## Key Pitfalls

1. **Forgetting `@dbIsolationPerTest`** — one transaction per class, state bleeding between methods, order-dependent failures.
2. **`--user-email=…` on `oro:install --env=test`** — silently ignored; the only supported path is `oro_test_framework.install_options`.
3. **Assuming fixtures share EM state** — the EM is cleared after **each** fixture, so entities from A are detached when B runs. `InitialFixtureInterface` on those that must survive.
4. **Bundle aliases for entity FQCNs** — deprecated in Doctrine; `Product::class` everywhere.

## See Also

`references/`: `webtestcase-helpers.md` (signatures) · `install-options.md` · `fixtures.md` · `acl-patterns.md` (HTML vs JSON 403, limited-user setup) · `grid-testing.md` · `v6.1.md` · `v7.0.md`

