Skip to content

IBX-12606: Migrated the test suites to PHPUnit 11 - #2093

Open
ViniTou wants to merge 2 commits into
6.0from
phpunit-11
Open

ViniTou wants to merge 2 commits into
6.0from
phpunit-11

Conversation

@ViniTou

@ViniTou ViniTou commented Sep 15, 2026

Copy link
Copy Markdown
Contributor
🎫 Issue IBX-12606
--- ---

Description:

CI status: MySQL/PostgreSQL integration (8.4) red on GetContentTreeChildrenTest / PostPostLoadSubtreeTest: REST snapshot expects <isBookmarked>true</isBookmarked>, actual false. Behaviour change from ibexa/core IBX-6773 (#476, "Fixed loading Bookmarks for non-accessible content items", merged to core 6.0 on 2026-09-17), which CI now resolves. Unrelated to this PR; admin-ui's fixture/snapshot needs a follow-up under its own ticket.

  • Migrates phpunit/phpunit ^9.5^11.5, matthiasnoback/symfony-dependency-injection-test
    ^5.0^6.0, adds symfony/phpunit-bridge ^7.4 to require-dev (was only present
    transitively before); dama/doctrine-test-bundle stays ^8.2 (already satisfied ^8.0).
    composer.lock is gitignored in this repo; the committed composer.lock-equivalent state was
    stale (sibling ibexa/* packages pinned to old resolutions) — a full
    composer update phpunit/phpunit symfony/phpunit-bridge matthiasnoback/* dama/* --with-all-dependencies
    refreshed the tree, incidentally moving symfony/http-foundation forward too (see below).
  • Migrates both phpunit.xml and phpunit.integration.xml to the 11.5 schema via
    --migrate-configuration plus hand fixes: added <extensions><bootstrap class="Symfony\Bridge\PhpUnit\SymfonyExtension"/></extensions> to both, converted the
    integration config's DAMA <extension class="…PHPUnitExtension"> to a <bootstrap> entry
    alongside it, added cacheDirectory=".phpunit.cache". beStrictAboutTodoAnnotatedTests was
    silently dropped by the migration tool (removed attribute in PHPUnit 10). Both configs validate
    against phpunit.xsd. Added .phpunit.cache/ to .gitignore (.phpunit.result.cache was
    already present).
  • Rector (PHPUnitSetList::PHPUNIT_100, PHPUNIT_110, ANNOTATIONS_TO_ATTRIBUTES on tests/
    with a temporary config, deleted after) converted 119 files: @dataProvider/@covers/@group
    annotations → #[DataProvider]/#[CoversClass]/#[Group] attributes, non-static provider
    methods without $this-dependencies → static, some createMock()createStub()
    conversions where no ->expects() followed. composer fix-cs cleaned up 11 stray docblock
    artifacts immediately after.
  • Renamed 1 abstract *Test.php base class to *TestCase.php (PHPUnit 11 defaults
    --fail-on-phpunit-warning to true, and an abstract class matching the runner's test-file
    naming pattern otherwise triggers a "no tests found" warning): BaseActionMenuBuilderTest
    BaseActionMenuBuilderTestCase (tests/lib/Menu/Action/, 2 subclasses updated). No deptrac
    baseline entries referenced its old FQCN.
  • Hand-fixed 3 Twig integration test extensions (EmbeddedItemEditFormExtensionTest,
    FocusModeExtensionTest, LocationExtensionTest) that overrode the old, non-static, deprecated
    getFixturesDir() instead of Twig\Test\IntegrationTestCase's new static
    getFixturesDirectory() (the abstract base's getTests() still falls back to the legacy method
    with a triggered deprecation, per Twig 3.13) — switched all three to override the static method,
    matching the two sibling files in the same directory that already did.
  • Hand-fixed ~10 non-static data providers across 8 files (ContentTreeChildrenQueryArgumentResolverTest,
    ContentTranslationMapperTest, UDWBasedValueViewTransformerTest, UserTransformerTest,
    VersionInfoTransformerTest, VersionListActionMenuBuilderTest + its shared base
    BaseActionMenuBuilderTestCase, LimitationResolverTest, IsProfileAvailableTest,
    DamWidgetTest) that Rector could not make static because they called $this->-dependent
    helpers. Where the helper only built plain value objects or self::createStub()-based doubles,
    made the whole call chain static. Where a double needed configured expectations that also had
    to stay identical across the provider and the instance method (VersionsTabVisibilityTest),
    switched to a private static ?Content $exampleContent cache built once via self::createStub()
    so both sides reference the same object. Two providers built doubles with real ->with()
    argument matchers or ->expects() call-count assertions
    (UDWBasedValueViewTransformerTest::createLocation, DamWidgetTest's content-type doubles) —
    Stub's InvocationStubber has no with() (PHPStan-caught), and the assertions were dead code
    under PHPUnit 9 anyway (the provider ran on a throwaway instance, so nothing was ever verified);
    replaced both with real Ibexa\Core\Repository\Values\Content\Location /
    Ibexa\Core\Repository\Values\ContentType\ContentType objects (real __get/hasFieldDefinition
    behaviour, no mocking needed).
  • Hand-fixed a genuine, pre-existing bug surfaced by making DamWidgetTest's provider static and
    PHPStan resolving the (now class-string, not doc-comment) #[CoversClass] attribute: it pointed
    at Ibexa\AdminUi\UI\Config\Provider\Module\ImagePicker, a class that does not exist in this
    test's own directory — corrected to DamWidget, the class actually under test.
    LocationIsNotSubLocationValidatorTest/LocationIsWithinCopySubtreeLimitValidatorTest used the
    removed MockBuilder::setMethodsExcept(['__get']); replaced with
    onlyMethods(array_values(array_diff(get_class_methods(Location::class), ['__get']))), which
    reproduces the old "mock everything except __get" semantics exactly.
  • Hand-fixed a real, pre-existing test-isolation bug in SetViewParametersListenerTest: Rector's
    WithConsecutiveRector turned a bare ->method('getParameter')->withConsecutive(...) (no
    ->expects(), so no call-count was ever enforced) into ->expects($matcher = self::exactly(2))->method(...)->willReturnCallback(...). That shared setUp() mock is only
    exercised by 1 of 7 test methods in the class; the other 6 started failing teardown verification
    ("expected 2 times, actually called 0 times"). Relaxed to self::any(), preserving the original,
    pre-Rector behaviour.
  • Hand-fixed 3 Symfony Request full mocks (getMockBuilder(Request::class)->getMock() in
    InContextTranslationListenerTest, RequestListenerTest, RequestLocaleListenerTest) that
    fatal with "Typed property Request::$attributes must not be accessed before initialization" —
    the mock's real constructor calls $this->initialize(...), which resolves to the mocked
    (stubbed-to-null) initialize(), so the typed $attributes property is never actually set; this
    was masked before by a stale local symfony/http-foundation resolution and surfaced once the
    composer bump above refreshed it to the version this branch's composer.json actually
    constrains. Fixed by assigning $request->attributes = new ParameterBag(); right after building
    each mock, restoring the pre-existing behaviour without weakening any of the mocks' expects()
    assertions.
  • Hand-fixed 2 remaining explicit deprecations: ->will(self::returnCallback(...))
    ->willReturnCallback(...) (ContentProxyCreateDraftListenerTest), ->will(self::returnValue(...))
    ->willReturn(...) (ContentTypeFormProcessorTest).
  • Added explicit closure return types (: void, : array) to 5 willReturnCallback closures
    Rector's WithConsecutiveRector left untyped (PHPStan missingType.return/Ibexa.requireClosureReturnType),
    across AdminUiFormsTest (×3), SubtreeOperationsTest, ContentTypeFormProcessorTest.
  • Verify: unit suite --display-phpunit-deprecations: 704 tests, 0 failures/errors, 10
    remaining getMockForAbstractClass() deprecations across 5 files (deprecated in 12, still
    allowed in 11 — matches the recipe's accepted exception; no replacement API exists yet).
    Integration: --list-tests clean, run 35 tests / 34 green — 1 pre-existing, environmental error
    unrelated to this migration (TranslationTest::testTranslation needs a Node.js
    @typescript-eslint/typescript-estree install that isn't present locally; reproduces identically
    on origin/6.0 with PHPUnit 9). 6 PHPUnit Deprecations in the integration run ("Metadata found
    in doc-comment") come from ibexa/test-rest's own BaseRestWebTestCase/WebTestCase
    @dataProvider doc-comments — vendor-owned, resolves with the ibexa/test-rest PHPUnit 11 wave
    PR, not fixable here.
  • vendor/bin/phpstan analyse --no-progress: 0 errors (no baseline in this repo, no ignores
    added). composer check-cs: 0 of 1239 files fixable. vendor/bin/rector process --dry-run --clear-cache: clean (one pre-existing, unrelated deprecated-skip-rule warning present on
    origin/6.0 too). vendor/bin/deptrac analyse --no-progress: 0 violations, 28 skipped
    (unchanged from baseline — the renamed abstract base isn't referenced in
    deptrac.baseline.yaml). No removed PHPUnit CLI flags (-v/--verbose) in composer.json
    scripts or CI workflows.

For QA:

N/A

Documentation:

N/A

->method('createView')
->willReturn(
$this->createMock(FormView::class)
$this->createStub(FormView::class)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we have an inconsistency. An Agent correctly applied in some cases self::createStub but there are ~189 cases with $this->createStub. createStub is static in PHPUnit 11.

@alongosz

alongosz commented Sep 17, 2026

Copy link
Copy Markdown
Member

Side note: code duplication valid, but not fixable within the scope, can be ignored. // sadly there's no tooling in Sonar to temporarily ignore code duplicates, unlike other errors.

@ViniTou
ViniTou force-pushed the phpunit-11 branch 2 times, most recently from 7a09bf7 to eab59aa Compare September 17, 2026 10:55
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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.

3 participants