XML-based tests for Magento core and modules
This article explains what the Magento Functional Testing Framework actually does, how its declarative XML structure fundamentally differs from imperative Cypress or Playwright code, and in which project phases MFTF still remains the right choice. Developers get a clear breakdown of when backend tests with MFTF make more sense than modern JavaScript E2E tests for the storefront.
Table of Contents
- 1. What MFTF is and its role in the Magento ecosystem
- 2. MFTF versus Cypress and Playwright: declarative instead of imperative
- 3. The MFTF architecture: Test, ActionGroup, Data, Page, and Section
- 4. Generated code: how MFTF turns XML into PHP and Codeception
- 5. When MFTF still makes sense today
- 6. When a modern JS E2E tool is the better fit
- 7. A concrete MFTF test example, walked through
- 8. Generating and running MFTF tests
- 9. MFTF versus Cypress/Playwright, compared side by side
- 10. Summary
- 11. FAQ
1. What MFTF is and its role in the Magento ecosystem
The Magento Functional Testing Framework (MFTF) is Adobe's official framework for functional acceptance testing in Magento 2 and Adobe Commerce. It ships with every Magento core release, covers core functionality such as checkout, product management, customer accounts, and the admin area, and Adobe itself uses it to safeguard the platform code against regressions. Anyone building a module that interacts with core behavior finds thousands of existing tests in dev/tests/acceptance as a reference and a base to extend.
Unlike generic E2E frameworks, MFTF is explicitly tailored to the Magento domain: it already models concepts like admin grids, layered navigation, multi-website setups, and ACL roles as reusable building blocks. For agencies maintaining third-party modules or submitting their own extensions to the Marketplace, MFTF is therefore not an optional tool but part of the expected quality proof toward Adobe and the community.
2. MFTF versus Cypress and Playwright: declarative instead of imperative
The fundamental difference lies in the programming model: Cypress and Playwright are imperative, a test is JavaScript or TypeScript code that describes step by step what should happen, including conditions, loops, and arbitrary logic. MFTF is declarative, a test is defined as an XML document describing a sequence of named actions, without the test author needing to know a general-purpose programming language. That design choice noticeably lowers the entry barrier for QA teams without deep JavaScript knowledge.
MFTF also ships its own Magento-specific vocabulary of actions, such as fillField, waitForPageLoad, or seeInCurrentUrl, combined with predefined sections for standard pages like the admin login or the product form. Cypress and Playwright know none of this vocabulary, every selector and every wait condition has to be implemented on the project side. That makes modern JS tools more flexible, but noticeably more repetitive for Magento-typical flows like the admin login or saving a product, unless the team builds its own utility library first.
3. The MFTF architecture: Test, ActionGroup, Data, Page, and Section
MFTF structures tests into five XML file types with clearly separated responsibilities. A Section defines selectors for the UI elements of a page, a Page defines the URL and references its associated sections. An ActionGroup bundles a recurring sequence of interactions, such as a full admin login, into a parameterizable, reusable building block. A Test finally orchestrates action groups and individual actions into a complete test scenario with setup and teardown. Data XML files supply test data entities that get referenced inside tests instead of hardcoding values.
This modular separation pays off in large test suites: if a CSS selector in the admin product form changes, only the affected section needs updating, every test and action group referencing that section keeps working unchanged. The two examples below show an action group for the admin login and the matching section with the referenced selectors.
<?xml version="1.0" encoding="UTF-8"?>
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
<actionGroup name="AdminLoginActionGroup">
<annotations>
<description>Logs in to the Magento Admin Panel with valid credentials.</description>
</annotations>
<arguments>
<argument name="username" defaultValue="{{_ENV.MAGENTO_ADMIN_USERNAME}}"/>
<argument name="password" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/>
</arguments>
<amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdminLogin"/>
<waitForPageLoad stepKey="waitForLoginPage"/>
<fillField selector="{{AdminLoginFormSection.username}}" userInput="{{username}}" stepKey="fillUsername"/>
<fillField selector="{{AdminLoginFormSection.password}}" userInput="{{password}}" stepKey="fillPassword"/>
<click selector="{{AdminLoginFormSection.signInButton}}" stepKey="clickSignIn"/>
<waitForPageLoad stepKey="waitForDashboard"/>
</actionGroup>
</actionGroups>
<?xml version="1.0" encoding="UTF-8"?>
<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
<section name="AdminLoginFormSection">
<element name="username" type="input" selector="#username"/>
<element name="password" type="input" selector="#login"/>
<element name="signInButton" type="button" selector="button.action-login"/>
</section>
<section name="AdminProductGridSection">
<element name="productNameCell" type="text" selector="td.col-name" parameterized="true"/>
<element name="successMessage" type="text" selector="div.message-success"/>
</section>
</sections>
4. Generated code: how MFTF turns XML into PHP and Codeception
MFTF is not a test runner itself, it's a code generation layer on top of Codeception, PHP's best-known acceptance testing framework. When you run vendor/bin/mftf generate:tests, MFTF reads all the XML files under Test/Mftf/Test, ActionGroup, Section, Page, and Data, resolves references and merges across modules, and produces concrete PHP classes in the dev/tests/acceptance/tests/_generated directory. Each of these classes is a regular Codeception Cest that uses the Selenium or Chromedriver WebDriver API.
This intermediate step has a decisive practical effect: the generated code isn't an implementation detail you can ignore, it's often the fastest way during debugging to understand why a test fails, since stack traces point to concrete lines in the generated PHP. Developers should never manually edit the generated code, since it gets overwritten on every re-run of generate:tests, but reading it as a debugging aid is perfectly reasonable.
5. When MFTF still makes sense today
MFTF remains the first choice wherever tests are tightly coupled to Magento core or module internals: admin workflows, ACL permissions, complex product type configurations, multi-source inventory, or the interplay between several third-party modules in the backend. Anyone submitting a module to the Adobe Commerce Marketplace can barely avoid MFTF tests, since the Marketplace quality guidelines explicitly list functional test coverage as an approval criterion and reviewers use existing MFTF suites as a reference point.
MFTF is also valuable as a regression net against future Magento core updates: since Adobe itself runs thousands of MFTF tests against every new core release, module developers benefit from the same test infrastructure and can integrate their own suites into the same CI run. For agencies maintaining several existing clients on classic, non-Hyvä themes, MFTF is often the only practical option too, because UI components and Knockout.js widgets are already covered by matching sections that a freshly set up JS test suite would otherwise have to rebuild from scratch.
6. When a modern JS E2E tool is the better fit
For the storefront of a Hyvä store, the recommendation flips. Hyvä relies consistently on Tailwind CSS and Alpine.js, a frontend stack for which MFTF has neither predefined sections nor any awareness of Alpine's internal state handling. Every interaction with an x-data component state would have to be reconstructed in MFTF via generic CSS selectors, whereas Cypress or Playwright operate directly in the same browser context real users interact with, and benefit from an active, well-maintained tooling community.
The feedback loop clearly favors JS tools too: a Cypress test runs in seconds in an interactive watch mode with time-travel debugging and live DOM snapshots, while an MFTF test first has to be generated and then run through a full Selenium session, which noticeably adds time per test run. Playwright adds native cross-browser support, network mocking, and visual regression testing with pixel comparison on top, all capabilities that aren't available in the MFTF ecosystem, or only through workarounds.
7. A concrete MFTF test example, walked through
The following example shows a complete MFTF test that creates a simple product as an admin user and verifies the success message. The annotations block documents feature, story, title, description, and severity, metadata MFTF also uses for test selection by group. The before block references the previously shown AdminLoginActionGroup, so the login flow doesn't need to be rewritten in every single test.
The actual test body fills form fields via referenced section selectors, clicks the save button, and uses see to verify that the expected success message appears in the DOM, functionally comparable to assertSee in classic Codeception tests. The after block ensures a clean logout at the end of the test, regardless of whether the test passed or failed.
<?xml version="1.0" encoding="UTF-8"?>
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
<test name="AdminCreateSimpleProductTest">
<annotations>
<features value="Catalog"/>
<stories value="Create Product"/>
<title value="Admin can create a simple product"/>
<description value="Verifies that an admin user can create a simple product and see the success message."/>
<severity value="CRITICAL"/>
<group value="catalog"/>
</annotations>
<before>
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
</before>
<amOnPage url="{{AdminProductNewPage.url}}" stepKey="navigateToNewProduct"/>
<waitForPageLoad stepKey="waitForProductForm"/>
<fillField selector="{{AdminProductFormSection.productName}}" userInput="Test Product 123" stepKey="fillProductName"/>
<fillField selector="{{AdminProductFormSection.productSku}}" userInput="test-product-123" stepKey="fillProductSku"/>
<fillField selector="{{AdminProductFormSection.productPrice}}" userInput="19.99" stepKey="fillProductPrice"/>
<click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/>
<waitForPageLoad stepKey="waitForSave"/>
<see selector="{{AdminProductGridSection.successMessage}}" userInput="You saved the product." stepKey="assertSuccessMessage"/>
<after>
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/>
</after>
</test>
</tests>
8. Generating and running MFTF tests
The MFTF workflow always runs in two stages: generation, then execution. vendor/bin/mftf generate:tests translates all the XML sources into PHP and places them in the generated directory, optionally scoped to a specific test group or a single module. Only after that can Codeception actually run the generated tests against a running Magento instance and a configured Selenium or Chromedriver endpoint.
For day-to-day development work, a tight, targeted cycle pays off: generate and run a single test or a single group instead of rebuilding the entire suite on every change, which can take several minutes for larger modules. The environment configuration in .env and MFTF-specific configuration files centrally controls the base URL, admin credentials, and the browser endpoint for the whole project.
# Generate the PHP/Codeception test suite from MFTF XML sources
vendor/bin/mftf generate:tests
# Run a single generated test against the configured Selenium/WebDriver
vendor/bin/mftf run:test AdminCreateSimpleProductTest
# Run an entire test group, e.g. all catalog-related tests
vendor/bin/mftf run:group catalog
# Regenerate and run in one step, cleaning stale generated code first
vendor/bin/mftf generate:tests --force
vendor/bin/mftf run:test AdminCreateSimpleProductTest --remove
9. MFTF versus Cypress/Playwright, compared side by side
Both approaches are valid in their respective context, the table below lines up the most important decision dimensions. One thing to keep in mind: no row means a tool is categorically worse, only that it's structurally at a disadvantage in that particular scenario.
| Aspect | MFTF | Cypress/Playwright | Recommendation |
|---|---|---|---|
| Language/approach | Declarative XML, unfamiliar to frontend teams | JS/TS, familiar syntax | Favor JS tools for frontend devs |
| Feedback loop | Generation plus a full Selenium session | Seconds, interactive watch mode | Use Cypress/Playwright for fast iteration |
| Magento vocabulary | Huge library of actions/sections | Has to be built from scratch per project | Use MFTF for core-adjacent tests |
| Marketplace compliance | Expected quality proof | Not part of the official process | Use MFTF for module submissions |
| Hyvä storefront testing | No awareness of Alpine.js state | Native browser interaction, visual regression | Use Cypress/Playwright for storefront E2E |
As a contrasting example, the code below applies the same underlying idea, proving success after an action, this time as a Playwright test for a cart flow on a Hyvä storefront, noticeably more compact than the equivalent MFTF XML, but without its built-in Magento vocabulary.
import { test, expect } from '@playwright/test';
// Equivalent storefront flow written imperatively for a Hyvä theme frontend
test('customer can add a simple product to the cart', async ({ page }) => {
await page.goto('/test-product-123.html');
await page.getByRole('button', { name: 'Add to Cart' }).click();
// Hyvä updates the mini cart via Alpine.js state, no full page reload
await expect(page.locator('[data-cart-count]')).toHaveText('1');
await expect(page.getByText('You added Test Product 123 to your shopping cart.')).toBeVisible();
});
In practice, most Magento projects do best with a deliberate split: MFTF for backend regression, module integrations, and wherever Marketplace compliance requires it, Cypress or Playwright for everything customers actually see and interact with in the Hyvä frontend. This split avoids duplicate test coverage and puts each tool exactly where it holds a structural advantage.
Mironsoft
MFTF, Cypress, and Playwright test strategy for Magento and Hyvä projects
Need a solid test strategy for your Magento project?
We build MFTF suites for backend and module regression, set up Cypress or Playwright for your Hyvä storefront, and wire both into a CI/CD pipeline that combines fast feedback with solid coverage.
MFTF test suites
Building admin, module, and Marketplace compliance tests
Hyvä storefront E2E
Setting up Cypress/Playwright for checkout and purchase flows
CI/CD integration
Wiring both test layers into your pipeline automatically
10. Summary
MFTF solves a different problem than Cypress or Playwright: it describes tests declaratively in XML, generates PHP code for Codeception from it, and ships an extensive, Magento-specific vocabulary of actions and sections for backend and module testing. The Test, ActionGroup, Data, Page, and Section architecture allows clean reuse across large test suites, though at the cost of a slower feedback loop compared to modern JavaScript tools.
For a Hyvä store's storefront, a modern E2E tool remains the better choice, since it operates directly in the same browser context real customers use, delivers faster feedback, and natively understands Alpine.js interactions. The most pragmatic solution for most Magento projects isn't an either-or decision, but a deliberate combination: MFTF for core and module regression, a JS tool for everything customers actually experience in the frontend.
MFTF and Modern E2E Tools, the Essentials at a Glance
XML instead of JavaScript
MFTF describes tests declaratively and generates runnable PHP/Codeception code from them.
Modular architecture
Test, ActionGroup, Data, Page, and Section fit together as reusable building blocks.
Where MFTF fits
Core/module regression, admin workflows, and Marketplace quality gates.
Where Cypress/Playwright fits
Hyvä storefront, fast iteration, visual regression, and cross-browser testing.