
Oro Behat Testing
- 3 installs
- 2 repo stars
- Updated July 22, 2026
- netresearch/orocommerce-skill
Writes and configures OroCommerce 6.1 Behat integration tests against a local app: suites, contexts, element and page objects, Alice YAML fixtures, and behat.yml setup.
About
Covers authoring and configuring OroCommerce 6.1 Behat suites, including auto-discovery vs symfony_bundle registration, custom Element/page-object wiring, shared contexts, and Alice fixtures. A developer uses it when building or configuring local-app Behat tests for an Oro bundle.
- Canonical Tests/Behat/behat.yml with suites, elements, and pages
- Auto-discovery vs manual symfony_bundle suite registration
Oro Behat Testing by the numbers
- 3 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,649 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/netresearch/orocommerce-skill --skill oro-behat-testingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 2 |
| Last updated | July 22, 2026 |
| Repository | netresearch/orocommerce-skill ↗ |
What it does
Writes and configures OroCommerce 6.1 Behat integration tests against a local app: suites, contexts, element and page objects, Alice YAML fixtures, and behat.yml setup.
Files
OroCommerce v6.1 Behat Integration Testing
Oro extends Behat with auto-discovered suite configuration, element abstractions over Mink, Alice fixture loading, feature-tag-driven service mocking, and automatic database isolation. A bundle's Behat assets live under Tests/Behat/ — suite config, contexts, elements, page objects, and Alice fixtures. The entry point is the bundle's Tests/Behat/behat.yml, which Oro's extension discovers at boot time. No per-project registration is required for the common case.
Canonical Suite Config
This is the reference Tests/Behat/behat.yml combining suite, shared contexts, elements, and a page object:
oro_behat_extension:
shared_contexts:
- Oro\Bundle\TestFrameworkBundle\Tests\Behat\Context\OroMainContext
suites:
AcmeDemoBundle:
contexts:
- Oro\Bundle\DataGridBundle\Tests\Behat\Context\GridContext
- Acme\Bundle\DemoBundle\Tests\Behat\Context\FeatureContext
paths:
- '@AcmeDemoBundle/Tests/Behat/Features'
elements:
Demo Login Form:
selector: '#login-form'
class: Oro\Bundle\TestFrameworkBundle\Behat\Element\Form
options:
mapping:
Username: '_username'
Password: '_password'
pages:
Demo Dashboard:
class: Acme\Bundle\DemoBundle\Tests\Behat\Page\DemoDashboard
route: 'acme_demo_dashboard'Two Suite Registration Forms
Oro supports both auto-discovery and manual symfony_bundle registration. Auto-discovery (the hero example above) works whenever the suite key matches a registered bundle name and paths points to a @BundleName/... path inside that bundle. Manual form is necessary when the suite name differs from the bundle, when you want multiple suites in one bundle, or when paths are non-standard:
oro_behat_extension:
suites:
MyCustomSuite:
type: symfony_bundle
bundle: AcmeDemoBundle
contexts:
- Acme\Bundle\DemoBundle\Tests\Behat\Context\ImportContext
paths:
- '@AcmeDemoBundle/Tests/Behat/Features/Import'See references/suite-config.md for the full shape including shared_contexts, nested xpath locators, element delegation, and embedded form mapping.
behat.yml.dist vs behat.yml
The project root behat.yml.dist is committed and holds shared defaults (Mink base_url, browser profiles, formatters). A local behat.yml is gitignored and overrides per-developer settings — most commonly base_url for non-default Docker port mappings and Chrome binary paths. Oro's bin/behat loads behat.yml if present, otherwise falls back to behat.yml.dist. Never commit behat.yml; it will break other developers' environments.
A separate but equally important file is config/config_behat_test.yml — this is application-level config (not Behat extension config) that takes effect when a feature is tagged @behat-test-env and the runner is invoked with --behat-test-env. Use it to swap service implementations for mocks or to activate test-only bundles. See references/feature-tag-mocking.md.
Fixtures and References
Alice YAML fixtures live in Tests/Behat/Features/Fixtures/ and load via a tag on the feature: @fixture-OroUserBundle:user.yml. The colon between bundle and filename is mandatory — omitting it silently fails to load the fixture. Query parameters ?user=admin and ?user_reference=xss_user apply the fixture under a specific security context. Four references exist without any fixture: @admin, @adminRole, @organization, @business_unit. Custom references are registered via a service tagged oro_behat.reference_repository_initializer. Details including inline fixtures and cross-bundle include: directives in references/fixtures.md.
Running Tests
The canonical invocation matching Oro's own Jenkins pipeline is:
bin/behat -vv -f pretty -o std -f junit -o var/logs/behat --strict \
--consumers=2 -s AcmeDemoBundleThe dual formatter (pretty for humans, junit for CI artifacts) is standard — drop it and you lose test result collection in CI. --strict fails on undefined or pending steps. -s AcmeDemoBundle scopes to one suite. --consumers=2 runs the MQ consumer layer inside the Behat process — note this coexists with a long-running consumer container in a full stack; both layers exist simultaneously. To discover step definitions with full examples use bin/behat -di -s AcmeDemoBundle | grep -i "cart"; -dl gives the shorter list-only form. See references/chrome-setup.md for the Chrome flags required to run headless in Docker and references/v6.1.md for the full oro:install command Oro's CI uses to provision the test database.
Feature Tag Mocking
Define candidate mocks in Tests/Behat/parameters.yml and use oro_test.behat.feature_tag_aware_factory to pick one based on feature/scenario tags. At runtime, a feature tagged @use-paypal-mock gets the PayPal stub; an untagged feature falls back to the default. This is the supported way to stub external APIs in Behat — never hand-roll network interception. Details in references/feature-tag-mocking.md.
Key Pitfalls
1. `@fixture-file.yml` without the bundle prefix — Alice silently loads nothing, the scenario runs against an empty database, and you chase missing-entity errors instead of the real cause. Always @fixture-BundleName:file.yml. 2. Skipping `shared_contexts` while adding a new suite — Contexts listed there propagate automatically into every suite. Omit it and your new suite quietly lacks OroMainContext, so core steps like "I login as admin" throw UndefinedStep at runtime, not config time. 3. No Elasticsearch isolator and no MessageQueue isolator by design — Oro's isolation package resets the database between scenarios but does not reset the search index or the MQ state. Features that index products or emit messages leak state into the next scenario. Order scenarios defensively and flush indices explicitly when it matters. 4. Raw CSS selectors embedded in step definitions — works today, fragile tomorrow when the template changes. Define an Element in behat.yml and reference it by name; the selector lives in one place and breaks loudly, not in twenty step files. 5. `--skip-isolators-tag` does not exist in 6.1 — older Oro docs and community posts reference it but the 6.1 CLI rejects it as an unknown option. The current flag is --skip-isolators (kills all isolation) for e2e runs; leave isolation on for local integration runs.
See Also
references/suite-config.md— Both suite registration forms,shared_contexts, element YAML with nested xpath and delegation, page objects,behat.yml.distvsbehat.ymlreferences/fixtures.md— Alice locations,@fixture-Bundle:file.ymlsyntax, security query params, built-in references,ReferenceRepositoryInitializer, inline and cross-bundle fixturesreferences/feature-tag-mocking.md—parameters.ymlfactory pattern,config/config_behat_test.ymlactivation, runtime tag resolutionreferences/chrome-setup.md— ChromeDriver install and flags, Oro Chrome extension mount, headless Docker flags, PHP memory limitsreferences/v6.1.md— Mailcatcher env vars,oro:installinvocation,--consumersdefault, isolation gapsreferences/v7.0.md— 7.1-dev deltas: PHP 8.1+, Alice(unique)deprecation, formal tag-aware mocking, ChromeDriver pinning
Chrome and ChromeDriver Setup for Behat
Oro's Behat tests drive a real Chrome instance via Mink + ChromeDriver. A handful of settings are non-negotiable for Docker and headless runs — skipping them causes flaky "element not found" errors that look like test bugs but are actually browser misconfigurations.
ChromeDriver Installation
ChromeDriver must match the installed Chrome major version. In 6.1 the common pattern was "install latest"; in 7.1-dev Oro's own CI pins a specific version (CHROME_DRIVER_VERSION=142.0.7444.175) to avoid surprise breakage. On a dev box:
# macOS
brew install --cask chromedriver
# Linux (Debian/Ubuntu)
apt-get install chromium-driver
# Verify
chromedriver --versionStarting ChromeDriver
ChromeDriver runs as a long-lived process on port 9515 (Behat connects over HTTP):
chromedriver --port=9515 --whitelisted-ips='' --url-base=/wd/hubPoint Mink at it in behat.yml.dist:
default:
extensions:
Behat\MinkExtension:
base_url: 'http://dev.oro.local'
browser_name: chrome
chrome:
api_url: 'http://localhost:9515'Mandatory Headless Flags
Oro's own Jenkins pipeline passes these flags to every headless Chrome run. All of them are load-bearing for Docker:
--headless
--no-sandbox
--disable-dev-shm-usage
--disable-extensions
--no-pings
--window-size=1920,1080
--load-extension=vendor/oro/platform/src/Oro/Bundle/TestFrameworkBundle/Resources/chrome-extension--no-sandboxis required inside a Docker container running as root; without it Chrome refuses to launch.--disable-dev-shm-usagepoints temp storage at/tmpinstead of/dev/shm— Docker's default 64MB/dev/shmis too small for Chrome and crashes silently under load.--window-size=1920,1080— Oro's default layouts assume a desktop viewport. Narrower windows trigger the mobile menu and break selectors that target desktop-only elements.--disable-extensionsdisables user extensions but does NOT disable--load-extension, which loads Oro's own.
Oro's Custom Chrome Extension
Oro ships a Chrome extension at vendor/oro/platform/src/Oro/Bundle/TestFrameworkBundle/Resources/chrome-extension. It injects hooks that let Behat see in-flight AJAX requests and page readiness state — without it, waitForAjax() has nothing to wait on and tests race against unloaded DOM.
In Docker: mount the vendor/ tree into any separate Chrome/selenium container, or run Chrome in the same container as PHP so the path resolves. Vanilla selenium/standalone-chrome images break without the mount.
PHP memory_limit = -1
Oro's test Docker image sets PHP memory_limit = -1 (unlimited). This is non-optional — Behat + the test kernel + Alice fixture loading routinely cross 512MB on realistic feature runs. Capped memory causes scenarios to die mid-fixture-load with confusing OOM traces. Mirror it in php.ini for any test container you build yourself.
Feature-Tag-Aware Mocking
Oro's Behat runner can swap service implementations based on tags on the feature or scenario being executed. This is the supported extension point for stubbing external APIs (payment gateways, shipping rate providers, ERP clients) without hand-rolling network interception.
The Factory Pattern
Define candidate services in Tests/Behat/parameters.yml and use oro_test.behat.feature_tag_aware_factory as the factory for the service alias the application code actually depends on:
# Tests/Behat/parameters.yml
parameters:
acme_payment.client.class: Acme\Bundle\PaymentBundle\Client\PaymentClient
services:
acme_payment.test.client.default_mock:
class: Acme\Bundle\PaymentBundle\Tests\Behat\Stub\DefaultMockClient
acme_payment.test.client.paypal_mock:
class: Acme\Bundle\PaymentBundle\Tests\Behat\Stub\PayPalMockClient
acme_payment.test.client.stripe_mock:
class: Acme\Bundle\PaymentBundle\Tests\Behat\Stub\StripeMockClient
acme_payment.client:
class: Acme\Bundle\PaymentBundle\Client\PaymentClientInterface
factory: '@oro_test.behat.feature_tag_aware_factory'
arguments:
- '@acme_payment.test.client.default_mock'
- ['@acme_payment.test.client.paypal_mock', 'use-paypal-mock']
- ['@acme_payment.test.client.stripe_mock', 'use-stripe-mock']At container build time, the factory reads the currently-running feature's tags (via Oro\Bundle\TestFrameworkBundle\Behat\BehatFeature — in 7.1 this is formally documented; in 6.1 it's an implementation detail). The first matching tag wins; untagged features get the first argument (the default).
Activating the Test Env
The factory only applies when Behat runs with the --behat-test-env flag and the feature carries the @behat-test-env tag:
@behat-test-env
@use-paypal-mock
Feature: PayPal checkout
Scenario: Customer pays with PayPal
Given I am on the checkout page
When I click "Pay with PayPal"
Then I should see "Order confirmed"bin/behat --behat-test-env -s AcmePaymentBundleFeatures not tagged @behat-test-env run against the normal service definitions — mocking is explicitly opt-in.
config/config_behat_test.yml
The application-level config that activates with --behat-test-env lives at config/config_behat_test.yml (not inside a bundle). Use it to import parameters.yml from bundles that need mocking and to toggle test-only bundle services:
# config/config_behat_test.yml
imports:
- { resource: '@AcmePaymentBundle/Tests/Behat/parameters.yml' }
- { resource: '@AcmeShippingBundle/Tests/Behat/parameters.yml' }
framework:
mailer:
dsn: 'smtp://127.0.0.1:1025'This file is not auto-discovered — it must exist at that exact path for Oro to load it.
Rule: Never Hand-Roll HTTP Interception
Don't reach for cURL wrappers or monkeypatch HttpClient in Behat tests. The factory pattern is the supported mechanism. Hand-rolled interception breaks the first time Oro upgrades Symfony HTTP components.
Fixtures and Entity References
Alice File Location
Fixture files live under {Bundle}/Tests/Behat/Features/Fixtures/ and use Nelmio Alice YAML syntax. Oro's OroAliceLoader wraps Alice with entity reference management, security context injection, and cross-bundle includes.
# src/Acme/Bundle/DemoBundle/Tests/Behat/Features/Fixtures/document.yml
Acme\Bundle\DemoBundle\Entity\Document:
document_{1..3}:
title: 'Document <current()>'
owner: '@admin'
organization: '@organization'Loading Fixtures via Feature Tag
A feature-level tag loads the fixture before any scenario runs. The colon between bundle name and filename is mandatory. Without the bundle prefix Alice silently loads nothing:
@fixture-AcmeDemoBundle:document.yml
Feature: Document list
Scenario: Admin sees all documents
Given I login as administrator
And I go to Documents
Then I should see 3 recordsStacking multiple fixture tags on one feature is supported and they load in declaration order:
@fixture-OroUserBundle:user.yml
@fixture-OroOrganizationBundle:BusinessUnit.yml
@fixture-AcmeDemoBundle:document.yml
Feature: Multi-fixture setupSecurity Context Query Parameters
Append ?user=admin or ?user_reference=xss_user to run the fixture load under a specific user context (affects ownership and ACL-driven defaults):
@fixture-OroSecurityTestBundle:commerce/shopping-list.yml?user_reference=xss_user
@fixture-OroSecurityTestBundle:commerce/saved-search.yml?user=admin
Feature: ACL-scoped fixturesuser= takes a username string; user_reference= takes an Alice reference name (from a prior fixture or a built-in).
Built-In References
Four references exist automatically, no fixture required:
@admin— the admin user created byoro:install@adminRole— the administrator role@organization— the default organization@business_unit— the default business unit
Use them directly in any fixture file or inline fixture without declaring them first.
Registering Custom References
For a custom reference that must be available across many fixtures (e.g. a payment method, a product family), implement Oro\Bundle\TestFrameworkBundle\Behat\Fixtures\ReferenceRepositoryInitializer and tag the service:
namespace Acme\Bundle\DemoBundle\Tests\Behat\Fixtures;
use Doctrine\Persistence\ManagerRegistry;
use Oro\Bundle\TestFrameworkBundle\Behat\Fixtures\ReferenceRepositoryInitializer;
use Oro\Bundle\TestFrameworkBundle\Test\DataFixtures\Collection;
class LoadDemoReferences implements ReferenceRepositoryInitializer
{
#[\Override]
public function init(ManagerRegistry $registry, Collection $referenceRepository): void
{
$family = $registry->getRepository(AttributeFamily::class)
->findOneBy(['code' => 'default_family']);
$referenceRepository->set('default_family', $family);
}
}# Tests/Behat/services.yml
services:
Acme\Bundle\DemoBundle\Tests\Behat\Fixtures\LoadDemoReferences:
tags:
- { name: oro_behat.reference_repository_initializer }Fixtures then refer to @default_family without any per-fixture setup.
Inline Fixtures in Gherkin
For small, feature-local data, skip Alice files entirely and use inline step definitions. Oro ships OroFixtureLoader steps that understand tables and Faker inline functions:
Given the following contacts:
| First Name | Last Name | Email |
| Joan | Anderson | <email()> |
| Karl | Smith | <firstName()>@example.com |
And I have 5 Cases
And there are two users with their own 7 Accounts<email()>, <firstName()>, <lastName()>, <phoneNumber()>, <company()> are Faker inline functions Alice evaluates at load time. Older Oro docs show username (unique): foo — use plain <firstName()> in new fixtures; the (unique) suffix is deprecated in 7.x.
Cross-Bundle Includes
One fixture can include: another from a different bundle. This is the idiomatic way to reuse a canonical customer or catalog setup:
# Tests/Behat/Features/Fixtures/checkout-setup.yml
include:
- '@OroCustomerBundle/Tests/Behat/Features/Fixtures/CustomerUserAmandaRCole.yml'
- '@OroPricingBundle/Tests/Behat/Features/Fixtures/PriceList.yml'
Acme\Bundle\DemoBundle\Entity\Order:
order_1:
customer: '@amanda_r_cole'
priceList: '@default_price_list'The @BundleName/... prefix is resolved via Symfony's FileLocator, same as suite paths.
Suite Configuration
Auto-Discovery Form
A suite key that matches a registered bundle name — and paths pointing at @BundleName/... — is picked up automatically by OroBehatExtension without any extra metadata. This is the 95% case and should be your default:
oro_behat_extension:
suites:
AcmeDemoBundle:
contexts:
- Acme\Bundle\DemoBundle\Tests\Behat\Context\FeatureContext
paths:
- '@AcmeDemoBundle/Tests/Behat/Features'The bundle-relative @AcmeDemoBundle syntax is resolved by Symfony's FileLocator at runtime — Oro doesn't hard-code bundle paths.
Manual symfony_bundle Form
When the suite name differs from any registered bundle, or when a single bundle hosts multiple logically distinct suites (e.g. split by feature area), use the explicit form:
oro_behat_extension:
suites:
AcmeImport:
type: symfony_bundle
bundle: AcmeDemoBundle
contexts:
- Acme\Bundle\DemoBundle\Tests\Behat\Context\ImportContext
paths:
- '@AcmeDemoBundle/Tests/Behat/Features/Import'
AcmeCheckout:
type: symfony_bundle
bundle: AcmeDemoBundle
contexts:
- Acme\Bundle\DemoBundle\Tests\Behat\Context\CheckoutContext
paths:
- '@AcmeDemoBundle/Tests/Behat/Features/Checkout'Both suites can then be run independently with bin/behat -s AcmeImport or bin/behat -s AcmeCheckout.
shared_contexts — Inherited Into Every Suite
The top-level shared_contexts key under oro_behat_extension lists contexts that are injected into every suite Oro discovers, across all bundles. This avoids repeating OroMainContext in every single behat.yml:
oro_behat_extension:
shared_contexts:
- Oro\Bundle\TestFrameworkBundle\Tests\Behat\Context\OroMainContext
- Oro\Bundle\FormBundle\Tests\Behat\Context\FormContext
suites:
AcmeDemoBundle:
contexts:
- Acme\Bundle\DemoBundle\Tests\Behat\Context\FeatureContext
paths:
- '@AcmeDemoBundle/Tests/Behat/Features'Shared contexts compose in addition to the per-suite contexts: list — they are not overridden.
Elements with Nested XPath Locators
Elements map human-readable names to CSS or XPath selectors. Simple mapping binds a form field to a raw field name; complex mapping uses type, locator, and optionally element to delegate to another Element class:
oro_behat_extension:
elements:
Payment Method Config Type Field:
class: Oro\Bundle\PaymentBundle\Tests\Behat\Element\PaymentMethodConfigType
Payment Rule Form:
selector: "form[id^='oro_payment_methods_configs_rule']"
class: Oro\Bundle\TestFrameworkBundle\Behat\Element\Form
options:
mapping:
Method:
type: 'xpath'
locator: '//div[@id[starts-with(.,"uniform-oro_payment_methods_configs_rule_method")]]'
element: Payment Method Config Type Field
Currency: 'oro_payment_methods_configs_rule[currency]'The element: key is delegation: when a step sets the Method field, Oro looks up the XPath node, instantiates PaymentMethodConfigType around it, and calls its setValue(). This keeps complex interaction logic out of step definitions.
Embedded Form Mapping
Some forms are rendered inside a wrapper div (e.g. contact forms on CMS pages). Declare the wrapper as the Element selector, then use embedded-id to tell the Form element which actual form tag to bind to:
oro_behat_extension:
elements:
CustomContactUsForm:
selector: 'div#page'
class: Oro\Bundle\TestFrameworkBundle\Behat\Element\Form
options:
embedded-id: embedded-form
mapping:
First name: 'custom_bundle_contactus_contact_request[firstName]'
Email: 'custom_bundle_contactus_contact_request[email]'Page Objects
Pages wrap a route and give Gherkin access to open and assert on it by name:
oro_behat_extension:
pages:
User Profile View:
class: Oro\Bundle\UserBundle\Tests\Behat\Page\UserProfileView
route: 'oro_user_profile_view'Usage: And I open User Profile View page or And I should be on User Profile View page. The step definitions in OroMainContext handle route resolution.
behat.yml.dist vs behat.yml
At the project root (not inside a bundle), behat.yml.dist is committed and defines base profiles — Mink base_url, Symfony kernel class, default formatter. A local behat.yml is gitignored and overrides per-developer settings. Oro's bin/behat prefers the local file if present:
# behat.yml.dist — committed
default:
extensions:
Behat\MinkExtension:
base_url: 'http://dev.oro.local'
browser_name: chrome
# behat.yml — gitignored, per-developer
default:
extensions:
Behat\MinkExtension:
base_url: 'http://dev.oro.local:8080'Never commit behat.yml; it will override base_url and browser config for everyone else.
Behat Testing — v6.1 Notes
Key Environment
- PHP 8.1+ required
- Symfony 5.4 LTS
- Chrome + ChromeDriver (version matched)
- PostgreSQL test database
- Mailcatcher for mail assertions
Mailcatcher Environment Variables
Oro's framework.mailer reads DSN and web URL from env at runtime. Defaults match the Mailcatcher Docker image:
ORO_MAILER_DSN=smtp://127.0.0.1:1025
ORO_MAILER_WEB_URL=http://127.0.0.1:1080/Behat mail assertions (I should see an email with subject "...") hit the Mailcatcher HTTP API on port 1080. Override both when the container runs on a different host.
Full oro:install Invocation (Oro CI)
Oro's Jenkins pipeline provisions the Behat database with this exact command. Copy it when setting up a new Behat environment:
bin/console oro:install \
--drop-database \
--user-name=admin \
--user-email=admin@example.com \
--application-url=http://dev-crm.local \
--user-firstname=John \
--user-lastname=Doe \
--user-password=admin \
--organization-name=ORO \
--env=prod \
--sample-data=n \
--timeout=3000--env=prod is deliberate — Behat runs against a production-mode kernel even though the fixtures are test data. --sample-data=n keeps the database clean; fixtures load per-feature instead. --timeout=3000 (50 min) accounts for initial entity-extend cache warmup on first install.
--consumers=N Default
The --consumers=2 default runs two MQ consumers inside the Behat process. This is formally documented in 7.1 but was effectively the default in 6.1 as well. It coexists with a long-running consumer container in full stacks — both layers exist simultaneously and serve different queues (isolated test queue vs. full-stack queues).
Isolation Gaps — No Elasticsearch, No MQ Isolator
The isolation package ships DoctrineIsolator, CacheIsolator, PricingStorageIsolator, and a few others. It does not ship an ElasticsearchIsolator or a MessageQueueIsolator. Search index state and pending MQ messages leak between scenarios by design. If your feature indexes a product and the next feature asserts search results, that's non-obvious test interdependence — handle it explicitly in your Background or Before hook.
--skip-isolators-tag Does Not Exist
Old Oro forum posts and StackOverflow answers mention --skip-isolators-tag=<tag>. This flag has been removed — current 6.1 CLI source has only --skip-isolators (blanket disable). Pasting the old flag gives an "unknown option" error, not a partial isolator skip. If you need per-tag skipping in 6.1, write a custom isolator that checks the feature's tags in its isApplicable().
Behat Testing — 7.1-dev Deltas
These are the changes introduced on master (7.1-dev) that matter when porting 6.1 Behat work forward or reading current Oro example code.
PHP 8.1+ Minimum Enforced by #[\Override]
7.x example contexts and elements carry the #[\Override] attribute on every overridden method. This is a PHP 8.3 feature that is advisory in 8.1 (attribute exists, runtime check in 8.3) — Oro 7.x code uses it consistently. Adding #[\Override] to a method that doesn't actually override anything is a compile-time error on PHP 8.3+. The practical effect: 7.x Behat code requires PHP 8.1+ minimum and is safest on 8.3.
Alice (unique) Suffix Deprecated
6.1 fixtures use username (unique): marge228 to force a unique value when Alice generates multiple entities from a template. 7.x deprecates this in favor of wrapping Faker directly:
# 6.1 style — still works in 7.1, deprecated
Oro\Bundle\UserBundle\Entity\User:
user_{1..10}:
username (unique): marge
# 7.1 style
Oro\Bundle\UserBundle\Entity\User:
user_{1..10}:
username: '<firstName()>_<current()>'The Faker wrapper (<firstName()>, <email()>, etc.) combined with <current()> gives unique values without the Alice-specific suffix syntax.
Feature-Tag-Aware Mocking Formally Documented
Oro\Bundle\TestFrameworkBundle\Behat\BehatFeature and its companion FeatureTagAwareFactory were effectively-internal in 6.1 — you could use them but the API wasn't in public docs. 7.1 documents both formally, including cache-backed tag resolution (tags are resolved once per feature file and cached for the duration of the run, not re-parsed per scenario). The pattern is the same as 6.1 (see feature-tag-mocking.md); what changed is the stability guarantee.
ChromeDriver Version Pinning
7.x docs and CI pin a specific ChromeDriver version (e.g. CHROME_DRIVER_VERSION=142.0.7444.175) instead of telling devs to install "latest". Chrome's release cadence creates driver/browser mismatches every 4-6 weeks; pinning avoids the surprise-breakage footgun. Match the pin to the Chrome version baked into your test Docker image.