Skip to content

Add unit and functional test suites - #87

Merged
loevgaard merged 9 commits into
3.xfrom
add-test-suites
Aug 31, 2026
Merged

Add unit and functional test suites#87
loevgaard merged 9 commits into
3.xfrom
add-test-suites

Conversation

@loevgaard

Copy link
Copy Markdown
Member

Adds a real test setup: a unit and a functional PHPUnit suite, with database-backed functional tests isolated by dama/doctrine-test-bundle (every test runs in a transaction that is rolled back).

Suites

unit (tests/Unit, no database) — Prophecy-based, BDD-style test names:

  • DI: Configuration, SetonoSyliusFacebookExtension, OverrideDefaultPixelProviderPass
  • Pixel model, PixelContext, DoctrineBasedPixelProvider, AdminMenuListener
  • PixelFixture, PixelExampleFactory, PixelType (Symfony TypeTestCase)
  • All Conversions API events (ProductViewed, CategoryViewed, ProductAddedToCart, OrderPlaced, CheckoutStarted)
  • All event subscribers, including the error-swallowing behaviour of EventSubscriber::track()

functional (tests/Functional, boots the test application) — a small FunctionalTestCase creates the channel/admin user/pixels it needs inside the transaction:

  • PixelRepository::findEnabledByChannel()
  • The setono_facebook_pixel fixture end-to-end
  • Admin CRUD through the Symfony test client: list, create, validation error, update, delete, unauthenticated redirect

Test application

  • DAMADoctrineTestBundle registered for test/test_cached, PHPUnit extension in phpunit.xml.dist
  • config/packages/test/{assets,webpack_encore}.yaml: functional tests do not need a frontend build (verified with public/build removed, i.e. CI conditions)
  • config/bootstrap.php: require_once the Composer autoloader with a sane path. The previous require dirname(__DIR__) . '../../../vendor/autoload.php' redeclared the autoloader under Infection, so every mutant died with a fatal error — the mutation job could never have produced a real result.

src/ — small behaviour-preserving simplifications to make branches testable

  • getTaxonName() in the product events: getMainTaxon() ?? getTaxons()->first()
  • FormatAmountTrait: amounts are integer cents, so $amount / 100 is exact and needs no round()
  • ViewCategorySubscriber: LimitIterator instead of a counter + break

Mutation testing

Infection now runs at 100% MSI (157 mutants killed, 0 escaped, 0 uncovered). Equivalent mutants are ignored explicitly in infection.json.dist: CastString on the defensive (string) casts of nullable getters in Event\* / the pixel provider, MethodCallRemoval for the type-narrowing Asserts that follow OptionsResolver/DQL validation, and ProtectedVisibility on FormatAmountTrait::formatAmount() (kept protected for subclasses of OrderBasedEvent).

CI

Unit job runs --testsuite unit; integration job runs --testsuite functional after creating the schema; coverage and mutation jobs now start MySQL and create the schema so the functional suite is included.

Dependencies (dev)

dama/doctrine-test-bundle ^7.3 (v8 requires PHPUnit ≥ 10, the plugin pack pins 9.6), symfony/browser-kit, symfony/css-selector.

Verified locally on PHP 8.3: 81 tests green, ECS, PHPStan (level max), Rector, Infection 100%.

https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS

- phpunit.xml.dist defines a "unit" suite (tests/Unit, no database) and a
  "functional" suite (tests/Functional, boots the test application). Functional
  tests run inside a transaction that dama/doctrine-test-bundle rolls back.
- Unit tests (Prophecy) cover the DI extension/configuration/compiler pass, the
  model, context, provider, admin menu, fixture + example factory, form type,
  all Conversions API events and all event subscribers.
- Functional tests cover PixelRepository::findEnabledByChannel(), the pixel
  fixture and the admin CRUD (list/create/validate/update/delete/auth) through
  the Symfony test client.
- Test application: register DAMADoctrineTestBundle in the test environments,
  do not require built frontend assets in tests, and fix config/bootstrap.php to
  require_once the Composer autoloader (the previous require redeclared the
  autoloader under Infection, turning every mutant into a fatal error).
- Small behaviour-preserving simplifications to make branches testable:
  main taxon fallback via getTaxons()->first(), integer cents divided by 100
  without rounding, LimitIterator instead of a counter/break.
- Infection now runs at 100% MSI; equivalent mutants (defensive string casts,
  type-narrowing asserts, trait method visibility) are ignored explicitly.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
The unit tests job runs the unit suite, the integration tests job runs the
functional suite after creating the schema, and the coverage and mutation jobs
start MySQL and create the schema so the functional tests are included.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
Comment thread src/Event/FormatAmountTrait.php
Comment thread tests/Application/config/packages/test/webpack_encore.yaml Outdated
Comment thread tests/Application/config/packages/test/assets.yaml Outdated
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (eef0385) to head (a777f5e).

Additional details and impacted files
@@              Coverage Diff               @@
##               3.x       #87        +/-   ##
==============================================
+ Coverage     0.00%   100.00%   +100.00%     
+ Complexity     113       110         -3     
==============================================
  Files           25        25                
  Lines          419       400        -19     
==============================================
+ Hits             0       400       +400     
+ Misses         419         0       -419     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread infection.json.dist Outdated
In CI the test container is already compiled by the schema creation steps, so
booting the kernel in the functional tests never executed
SetonoSyliusFacebookPlugin::build()/getSupportedDrivers() under coverage and
Infection reported their mutants as uncovered. Test the bundle class directly
instead of relying on the container cache state.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
payum/payum-bundle 2.7.0 replaced its XML routing files with YAML, which breaks
the @PayumBundle/Resources/config/routing/*.xml imports in Sylius 1.14's shop
routing; 2.7.1 restored the XML files as a backwards compatibility layer. The
"lowest" dependency resolution in CI landed on exactly 2.7.0, so the functional
tests could not build the router.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
…checks

The integration, coverage and mutation jobs now install Node (from
tests/Application/.nvmrc) and run yarn install && yarn build before the
functional tests, so the test environment no longer needs to disable Webpack
Encore's strict mode or drop the asset manifests.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
Without ignores the suite kills 165 of 186 mutants (MSI 88.7%); the remaining
21 are defensive (string) casts of nullable getters, type-narrowing asserts
after OptionsResolver/DQL validation and the trait method visibility.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
Running Infection single-threaded (as CI does) kills 162 of 186 mutants
(87.1%); the earlier 88.7% measured locally with four threads counted three
equivalent mutants as killed because parallel functional tests collided on the
same unique channel codes in the shared test database.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
Replaces the payum/payum-bundle version constraint: instead of going through
Sylius' XML-only payum.yml, the test application imports the same three Payum
routing files with a {xml,yaml} glob, so it works with payum-bundle 2.7.0
(YAML only) as well as with < 2.7 (XML only) and >= 2.7.1 (both).

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
…cation"

The require-dev constraint "payum/payum-bundle": "^2.6 !=2.7.0" is the
simpler way to keep payum-bundle 2.7.0 (YAML-only routing, incompatible with
Sylius 1.14's imports) out of the test application, and it keeps the test
application's shop routing identical to Sylius'.

This reverts commit b0d2b9a.

Claude-Session: https://claude.ai/code/session_013jh2QjzkxAJABPDYB1UiPS
@loevgaard
loevgaard merged commit 343617c into 3.x Aug 31, 2026
30 checks passed
@loevgaard
loevgaard deleted the add-test-suites branch August 31, 2026 10:55
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