Skip to content

test: migrate doctrine/graphql behat features to ApiTestCase#8205

Merged
soyuka merged 13 commits into
api-platform:mainfrom
soyuka:test/migrate-doctrine-behat
May 29, 2026
Merged

test: migrate doctrine/graphql behat features to ApiTestCase#8205
soyuka merged 13 commits into
api-platform:mainfrom
soyuka:test/migrate-doctrine-behat

Conversation

@soyuka
Copy link
Copy Markdown
Member

@soyuka soyuka commented May 28, 2026

Summary

Removes all remaining features/doctrine/*.feature behat scenarios and replaces them with PHPUnit ApiTestCase tests, mirroring the migration pattern from prior phases.

Phase 1 — trivial features (commit c69e2a4b9)

Behat feature PHPUnit test
handle_links.feature tests/Functional/Doctrine/LinkHandlerTest.php
issue5722/subresource_without_get.feature tests/Functional/SubResource/SubResourceWithoutGetTest.php
issue6175/standard_put_entity_inheritence.feature tests/Functional/Doctrine/MappedSuperclassPutTest.php
eager_loading.feature tests/Functional/Doctrine/EagerLoadingTest.php
separated_resource.feature tests/Functional/Doctrine/SeparatedResourceTest.php
multiple_filter.feature tests/Functional/Doctrine/MultipleFilterTest.php

EagerLoadingTest reads ApiPlatform\Tests\Fixtures\TestBundle\Doctrine\Orm\EntityManager::$dql to assert the produced DQL, mirroring behat's the DQL should be equal to step.

Phase 2 — filter features (commit a3dee79f7)

Behat feature PHPUnit test
boolean_filter.feature tests/Functional/Doctrine/BooleanFilterTest.php
exists_filter.feature tests/Functional/Doctrine/ExistsFilterTest.php
numeric_filter.feature tests/Functional/Doctrine/NumericFilterTest.php
range_filter.feature tests/Functional/Doctrine/RangeFilterTest.php
date_filter.feature tests/Functional/Doctrine/DateFilterTest.php
order_filter.feature tests/Functional/Doctrine/OrderFilterTest.php
search_filter.feature tests/Functional/Doctrine/SearchFilterTest.php

Scenario outlines map to PHPUnit's #[TestWith] attribute. Postgres- and SQLite-specific scenarios are gated via $this->isSqlite() / $this->isPostgres() checks.

Stats

vendor/bin/phpunit tests/Functional/Doctrine/114 tests, 376 assertions, 5 skipped (mongo-only scenarios), 0 failures.

GraphQL features (features/graphql/*.feature) are deliberately left out of this PR.

Test plan

  • CI green on default suite
  • CI green on USE_SYMFONY_LISTENERS=1
  • CI green on mongodb suite

@soyuka soyuka force-pushed the test/migrate-doctrine-behat branch 2 times, most recently from 66541c0 to 20a937d Compare May 28, 2026 14:52
soyuka added 7 commits May 29, 2026 16:02
Drop six behat scenarios in favor of PHPUnit ApiTestCase:

- handle_links.feature                          -> tests/Functional/Doctrine/LinkHandlerTest.php
- issue5722/subresource_without_get.feature     -> tests/Functional/SubResource/SubResourceWithoutGetTest.php
- issue6175/standard_put_entity_inheritence     -> tests/Functional/Doctrine/MappedSuperclassPutTest.php
- eager_loading.feature                         -> tests/Functional/Doctrine/EagerLoadingTest.php
- separated_resource.feature                    -> tests/Functional/Doctrine/SeparatedResourceTest.php
- multiple_filter.feature                       -> tests/Functional/Doctrine/MultipleFilterTest.php

EagerLoadingTest reads
ApiPlatform\Tests\Fixtures\TestBundle\Doctrine\Orm\EntityManager::\$dql
to assert the produced DQL, mirroring behat's "the DQL should be equal to" step.
Drop seven behat filter scenarios in favor of PHPUnit ApiTestCase:

- boolean_filter.feature  -> tests/Functional/Doctrine/BooleanFilterTest.php
- exists_filter.feature   -> tests/Functional/Doctrine/ExistsFilterTest.php
- numeric_filter.feature  -> tests/Functional/Doctrine/NumericFilterTest.php
- range_filter.feature    -> tests/Functional/Doctrine/RangeFilterTest.php
- date_filter.feature     -> tests/Functional/Doctrine/DateFilterTest.php
- order_filter.feature    -> tests/Functional/Doctrine/OrderFilterTest.php
- search_filter.feature   -> tests/Functional/Doctrine/SearchFilterTest.php

Scenario outlines map to PHPUnit's #[TestWith] attribute. Postgres-only
scenarios are env-gated via \$this->isSqlite()/isPostgres() checks.
Migrate features/graphql/*.feature (11 files, 147 scenarios) to
tests/Functional/GraphQl/*Test.php (140 tests, 100% scenario coverage —
6 authorization scenarios consolidated, 1 introspect-types covered by
IntrospectionTest).

Add src/GraphQl/Test/GraphQlTestTrait.php as reusable helper providing
executeGraphQl(), introspectSchema(), executeGraphQlMultipart() and
GraphQL-specific assertions.

Drop tests/Behat/GraphqlContext.php and remove the corresponding context
entries from behat.yml.dist.

Fix stale metadata-cache leakage between test classes:
- WithResourcesTrait::invalidateMetadataPools() clears the on-disk pool
  directories AND PhpFilesAdapter::$valuesCache (process-lifetime static
  memoisation of append-only pool files — disk clearing alone is not
  enough because the static cache keeps returning entries computed under
  a prior class's resource subset).
- SetupClassResourcesTrait::tearDownAfterClass calls ensureKernelShutdown
  so the next class boots a fresh container with the updated resource
  set and a re-warmed cache.

Without this fix, classes that register a superset of a previously-run
class's resources receive cached ResourceMetadataCollection entries
missing the link/relation entries for the not-yet-registered classes,
producing GraphQL responses with null sub-collections.
Behat features fully migrated to PHPUnit functional tests
(tests/Functional/**). Strip remaining behat scaffolding.

- composer.json: drop behat/behat, behat/mink, friends-of-behat/*,
  soyuka/contexts dev deps
- .github/workflows/ci.yml: remove behat, behat-symfony-next,
  windows-behat, behat-symfony-lowest, behat_listeners jobs; rename
  postgresql/mysql/mongodb/mercure (PHPUnit + Behat -> PHPUnit) and
  drop behat steps; switch mongodb/mercure coverage uploads to phpunit
- delete tests/Behat/*, behat.yml.dist, config_behat_{orm,mongodb}.yml
- move features/files/test.gif to tests/Functional/GraphQl/Fixtures/
- AppKernel: drop FriendsOfBehatSymfonyExtensionBundle, DoctrineContext
  loader, stale behat compat comment
- CONTRIBUTING.md, AGENTS.md, tests/AGENTS.md, .phpactor.json,
  phpunit.xml.dist: remove behat references
Address CS, PHPStan, MongoDB and deprecation issues exposed by the
behat-to-PHPUnit migration:

- Run php-cs-fixer on migrated GraphQL test files
- Use self:: (not static::) for private invalidateMetadataPools()
  in WithResourcesTrait to satisfy phpstan
- Fix RecreateSchemaTrait::recreateSchema() Entity->Document rewrite
  to match the \Entity\ namespace segment only, preventing classes
  already in \Document\ (e.g. SeparatedEntity aliased as
  SeparatedDocument) from being rewritten to non-existent classes
- Branch on isMongoDB() in MutationTest to instantiate the correct
  FooEmbeddable (Document vs Entity)
- Expect the symfony/serializer 8.1 PartialDenormalizationException::
  getErrors() deprecation in tests that trigger
  collect_denormalization_errors
- Assign distinct shortNames to sub-collection ApiResource attributes
  on DummyProduct, DummyAggregateOffer, DummyOffer and Greeting to
  silence the 4.2 multiple-shortName deprecation; update
  SubResourceTest @context assertions accordingly
- Widen $files PHPDoc on executeGraphQlMultipart to accept UploadedFile
- MappingTest::testShouldMapBetweenResourceAndEntity: pass MappedDocument
  on MongoDB. RecreateSchemaTrait's Entity->Document rewrite produces
  Document\MappedEntity which does not exist (target is MappedDocument)
- MutationTest::testModifyNonWritablePropertyRejected: skip on MongoDB
  like the sibling testModifyNonWritableEmbeddedPropertyRejected; the
  scenario relies on the ORM-specific embeddable input behavior
- Add Document\RPCHandler messenger handler so MongoDB messenger RPC
  test has a matching handler (Entity\RPCHandler was the only one)
- Use Composer\InstalledVersions::satisfies() instead of method_exists()
  to detect symfony/serializer >= 8.1 in deprecation expectations;
  PHPStan resolves method_exists() statically and flagged it as
  alreadyNarrowedType
@soyuka soyuka force-pushed the test/migrate-doctrine-behat branch from 83d4851 to 0b6f81a Compare May 29, 2026 14:02
The conditional loading of phpunit.yml gated test resource subsetting
behind APP_PHPUNIT. Now that behat is gone the kernel is only booted
from phpunit (and from phpstan against the test container), so the
config can be loaded unconditionally and the env var dropped.

Ensure tests/Fixtures/app/var/resources.php exists before kernel boot
so cache:clear works on fresh checkouts (e.g. opensearch CI, which
runs cache:clear standalone without phpunit). Also harden
TestSuiteConfigCache::getHash() against a missing file.
@soyuka soyuka force-pushed the test/migrate-doctrine-behat branch from 0b6f81a to 8d3cdd3 Compare May 29, 2026 14:18
soyuka added 5 commits May 29, 2026 16:42
After commit 8d3cdd3 phpunit.yml loads unconditionally, so the
PhpUnitResourceNameCollectionFactory now decorates the cached factory
even for console commands run standalone (e.g. the OpenAPI export CI
step that runs cache:clear + api:openapi:export without phpunit). On a
fresh checkout resources.php is empty, which produced an empty `paths`
in the exported OpenAPI document and failed vacuum lint.

Make PhpUnitResourceNameCollectionFactory accept its decoratee via
.inner and fall back to the default discovery when no resources are
configured. Phpunit suites still get the subsetting because they
write resources.php before booting the kernel.
OverriddenOperationTest::testRpcMessengerOperationReturns202 was added
in e6922f1 with only an Entity-based messenger handler in
config_common.yml. In the mongodb env PhpUnitResourceNameCollectionFactory
swaps Entity\RPC for Document\RPC, so the dispatched message had no
handler and the endpoint returned 500. Mirror the messenger_with_inputs
override pattern and register the Document handler under the same
service id.
Second #[ApiResource] attribute on AbsoluteUrlDummy, NetworkPathDummy and
DummyResourceWithComplexConstructor shared the default shortName with the
primary resource, triggering the 4.2 duplicate shortName deprecation. Give
each subresource a distinct shortName to silence the warnings without
opting into `deduplicate_resource_short_names`.
Commit 8d3cdd3 dropped the APP_PHPUNIT gate on the wrong premise
that only phpunit boots the kernel. CI also boots the kernel
standalone via tests/Fixtures/app/console for lint:container,
cache:clear, redocly-lint and the OpenAPI export. Without the gate
phpunit.yml loaded in those runs too, wiring
PhpUnitResourceNameCollectionFactory against an empty resources.php
and producing an OpenAPI document with no paths (vacuum lint fail).

Restore the env-gated load: phpunit.xml.dist sets APP_PHPUNIT=true
so phpunit runs still get the subsetted resource list, while
standalone console boots fall back to the default discovery chain
and see every resource.

Revert the [] === classes fallback added in 4c8cbe6 along with
its \$decorated constructor argument — the factory is no longer
wired outside phpunit so the band-aid is unnecessary.
@soyuka soyuka changed the title test: migrate doctrine behat features to ApiTestCase test: migrate doctrine/graphql behat features to ApiTestCase May 29, 2026
@soyuka soyuka merged commit 4911cfd into api-platform:main May 29, 2026
102 of 108 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant