Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a comprehensive Behat extension for Foundry, packaged as a separate composer package (zenstruck/foundry-behat). The extension provides seamless integration between Foundry's factory system and Behat's behavioral testing framework.
Changes:
- Adds a complete Behat extension with step definitions for creating objects, making assertions, and managing fixtures
- Implements database reset strategies (scenario, feature, manual, disabled) with DAMA DoctrineTestBundle support
- Refactors kernel configuration signatures to use
ContainerConfiguratorfor Symfony compatibility - Introduces fixture story resolution system with the
FixtureStoryResolverclass - Updates enum fixtures to use backed enums (IntBackedEnum, StringBackedEnum) for better type safety
Reviewed changes
Copilot reviewed 98 out of 101 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Test/Behat/* | Core Behat extension implementation including contexts, listeners, object registry, and factory resolution |
| src/Story.php | Added event dispatching when state is added to stories for Behat integration |
| src/Command/LoadFixturesCommand.php | Refactored to use FixtureStoryResolver instead of raw arrays |
| tests/Fixture/*Kernel.php | Updated configureContainer signature to include ContainerConfigurator parameter |
| tests/Fixture/IntBackedEnum.php | Renamed from SomeEnum and converted to int-backed enum |
| tests/Fixture/StringBackedEnum.php | New string-backed enum for testing |
| tests/Fixture/Model/GenericModel.php | Added additional fields for comprehensive testing (dateMutable, bool, float, enums) |
| config/persistence.php | Updated service configuration for fixture story resolver |
| composer.json | Excluded Behat extension from main package classmap |
| docs/index.rst | Added extensive documentation for Behat integration (350+ lines) |
| .github/workflows/behat.yml | New CI workflow for testing Behat extension |
Comments suppressed due to low confidence (1)
tests/Fixture/IntBackedEnum.php:18
- The enum has been renamed from
SomeEnum(unit enum) toIntBackedEnum(int-backed enum) with values. This is a breaking change. Any existing code referencingSomeEnumwill break. Verify that all references have been updated, especially in tests and documentation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * | ||
| * @template T of object | ||
| */ | ||
| final class StateAddedToStory |
There was a problem hiding this comment.
I use this event to give a name to the objects created in story, so that we can access them the Behat features
| @@ -0,0 +1,26 @@ | |||
| #!/bin/bash | |||
There was a problem hiding this comment.
this script is used to symlink ./src into ./src/Test/Behat/vendor/zenstruck/foundry
this improves DX a lot while working on the Behat extension
| "symfony/polyfill-php84": "^1.33", | ||
| "symfony/polyfill-php85": "^1.33", | ||
| "symfony/string": "^6.4|^7.0", | ||
| "zenstruck/foundry": "dev-behat-extension" |
There was a problem hiding this comment.
| if (\class_exists(BehatServicesCompilerPass::class)) { | ||
| $container->addCompilerPass(new BehatServicesCompilerPass()); // @phpstan-ignore argument.type | ||
| } |
There was a problem hiding this comment.
this is the simpler way I found to plug the extension into our bundle. And it is the only place where the namespace Zenstruck\Foundry\Test\Behat is used in all Foundry's core repo
a7d251e to
8c9cfd7
Compare
* feature: Behat extension * refactor(behat): don't use subtree split approach * feat(behat): create FoundryContext * feat(behat): create object with properties * feat(behat): create multiple objects * feat(behat): handle -ToOne relation ships * feat(behat): transformer for last ids * feat(behat): handle fixtures as stories wip * feat(behat): can name fixtures from story based on story state * feat(behat): reset database by scenario * feat(behat): reset database by feature * feat(behat): introduce @resetDB tag * feat(behat): provide support for Dama * feat(behat): introduce @noResetDB tag * feat(behat): short syntax for referencing objets * feat(behat): handle correctly built in types * feat(behat): handle correctly enums * feat(behat): use main-dama as main testsuite * feat(behat): tests scenario with errors * refactor(behat): extract all normalization logic into FoundryCallFilter * minor(behat): only load behat services when needed * minor(behat): can load several fixtures on the same scenario * minor(behat): rename exceptions without suffix * tests(behat): test few behaviors with PHPUnit * fix(behat): manual strategy should not reset the DB before the first test * minor(behat): add behat file+line in exceptions * fix(behat): make the CI green
* refactor: src/Test/Behat as a subpackage * chore(behat): make tests phpunit pass * chore(behat): make phpstan & behat tests working
8c9cfd7 to
2e8c013
Compare
…CallFilter Extract the large array_map closure into 3 private methods: normalizeTableRow(), resolveExplicitObjectReference(), resolveObjectReferenceBasedOnPropertyType(). Use match expression instead of if/continue chain. Add null coalescing throw for array_shift and fix trailing whitespace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tNameResolver Replace nested array_any/array_find_key calls with a classToShortName cache built at boot() time, enabling O(1) lookups via isset() and direct key access. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make error messages more descriptive: include the number of rows received and suggest using the "there are X with" step when trying to create multiple objects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the (?!\d) negative lookahead inside the branch reset group, just before the unquoted \S+ alternative. This allows quoted names like "007" to start with a digit while still preventing ambiguity with count patterns for unquoted names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add AbstractUid support via toRfc4122() conversion for entities using UUID/ULID identifiers. Add unit test and Behat functional scenario. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1ca710a to
d93d3cb
Compare
Add a note explaining that the <ref(type, name)> syntax is a fallback mechanism for edge cases, and that automatic resolution should be preferred. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d93d3cb to
9c1b400
Compare
| #[Given('there is a(n) :factoryShortName with')] | ||
| #[Given('there is a(n) :factoryShortName :objectName with')] | ||
| #[Given('there is a(n) :factoryShortName called :objectName with')] | ||
| #[Given('there is a(n) :factoryShortName named :objectName with')] |
There was a problem hiding this comment.
| #[Given('there is a(n) :factoryShortName with')] | |
| #[Given('there is a(n) :factoryShortName :objectName with')] | |
| #[Given('there is a(n) :factoryShortName called :objectName with')] | |
| #[Given('there is a(n) :factoryShortName named :objectName with')] | |
| #[Given('there is a(n) :factoryShortName with:')] | |
| #[Given('there is a(n) :factoryShortName :objectName with:')] | |
| #[Given('there is a(n) :factoryShortName called :objectName with:')] | |
| #[Given('there is a(n) :factoryShortName named :objectName with:')] |
I think that with behat, it's common to finish a line with : when it's followed by a TableNode.
Examples in behat codebase or in soyuka context or even in the cucumber gherkin reference guide.
| class FoundryContext extends AbstractFoundryContext implements Context | ||
| { | ||
| #[\Override] | ||
| #[Given('there is a(n) :factoryShortName')] |
There was a problem hiding this comment.
Won't those strings be too generic ? It may override, or be overridden by other contexts ?
Shouldn't it contain a reference to foundry (or at least "fixture") ?
This is a "meta PR" for the whole feature of the Behat extension + context
@aegypius @loic425 @mpdude the extension is now 90% ready! It would be super nice if you could give your thoughts about it 🙏
don't bother to review the whole massive PR, but the docs and the example features will help you to grasp what it does.
We've finally decided to ship an external package
zenstruck/foundry-behat, mainly because Behat does not support SF 8.0 and it would have been a PITA to maintain the other way.Currently Foundry won't ship any way to create objects in "natural language" nor a solution to use so-called "state methods" out of the box, I gave it a try and the complexity increased drastically, but this could be a nice improvement for the future.
Another future evolution would be to handle
-ToManyrelations which are currently not handledfixes #1051
fixes #235