From MFTF to Playwright for Magento 2 E2E testing

The benefits of modern TypeScript E2E testing over the old XML-and-Selenium way


For years my end-to-end (E2E) tests on Magento 2 were written in MFTF, the Magento Functional Testing Framework. It ships with the platform and it does the job, but every test meant writing XML driven by Selenium underneath — verbose, slow, and awkward to debug. A while ago I moved that work to Playwright as my MFTF alternative, and I am not going back. Here is what actually changed, feature by feature.

The old way vs the new way

MFTF

the traditional way

  • Tests written in XML
  • Spread across many files
  • Selenium — slower, flakier
  • Debug from a screenshot
  • Coupled to Magento core

Playwright

the modern way

  • Tests written in TypeScript
  • One readable spec file
  • Auto-waiting, runs in parallel
  • Trace viewer time-travel
  • An independent dependency

Same goal — "does checkout still work?" — reached two very different ways.

The rest of this post is really just those rows, explained.

XML across many files vs one TypeScript spec

In MFTF a single “add to cart” test is spread over a test file, a section file for the selectors and a data file for the product — all XML, all cross-referenced by string keys:

xml
<!-- Test/Mftf/Test/AddToCartTest.xml -->
<test name="AddSimpleProductToCartTest">
    <amOnPage url="{{SimpleProduct.urlKey}}.html" stepKey="goToProduct"/>
    <waitForPageLoad stepKey="waitForProductPage"/>
    <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/>
    <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccess"/>
    <see userInput="You added Simple Product to your shopping cart." stepKey="assertSuccess"/>
</test>

The same test in Playwright is one file, and it reads like the steps a person would take:

ts
test('adds a product to the cart', async ({ page }) => {
  await page.goto('/simple-product.html');
  await page.getByRole('button', { name: 'Add to Cart' }).click();
  await expect(page.getByRole('alert')).toContainText('added');
});

No section file, no data file, no stepKey on every line. Your editor autocompletes it, TypeScript catches typos, and refactoring is a rename instead of a find-and-replace across XML.

Manual waits vs auto-waiting

Notice what is missing from the Playwright version: the waitForPageLoad and waitForElementVisible steps. Playwright auto-waits — a click waits for the element to be visible, enabled and stable first, and expect(...) retries until the assertion passes or times out. In MFTF (via Selenium) you add those waits by hand, and forgetting one is the classic source of a test that passes locally and fails in CI. Most of my flakiness simply disappeared.

A screenshot vs the trace viewer

This one changed how I debug. When an MFTF test fails in CI you typically get a screenshot and a stack trace and are left guessing. Playwright records a trace on the first retry — a full timeline you open locally and scrub through:

bash
npx playwright show-trace trace.zip

Every action, every network call, a DOM snapshot at each step, and the console. Instead of reproducing the failure you watch it happen. There is also a live UI mode (--ui) for writing tests and a code generator (--codegen) that writes selectors for you.

Slow and sequential vs fast and parallel

Playwright runs tests in parallel across worker processes out of the box, and the browser automation talks to Chromium directly rather than through the Selenium/WebDriver stack. On the suites I have migrated the wall-clock time dropped sharply — enough that running E2E on every push became realistic rather than a nightly chore.

Clean, isolated test data

The one habit worth keeping from MFTF is its data fixtures — never assume data “is already there”. Playwright makes this elegant with fixtures: each test seeds exactly what it needs through the Magento REST API, uses it, and destroys it afterwards, with no effort from the test author. I run them against a local Warden environment that mirrors production closely.

1SeedCreate a customer + product via the Magento REST API
2Run testFresh, isolated data; logged-in page ready
3TeardownDelete the customer + product via the API

Each test seeds its own data and destroys it — no shared state, nothing left in the database.

In the spec you never see the plumbing. You just ask for the fixture:

ts
test('shows my orders', async ({ authenticatedPage }) => {
  // a brand-new customer was created and logged in for you;
  // it is deleted automatically when the test ends.
  await authenticatedPage.goto('/customer/account/');
  await expect(authenticatedPage.getByRole('heading', { name: 'My Account' })).toBeVisible();
});
Seeding through the API is wonderful on local and staging and dangerous on production. It is worth making your helper throw if a write is attempted against a live store — that turns a scary mistake into a harmless failed test.

Not welded to Magento’s release cycle

Because MFTF ships with the platform, its version moves when Magento moves. Playwright is just an npm dev dependency you update on your own schedule, independent of the store’s Magento version. One less thing coupled to the upgrade.

The one thing MFTF still has

Fairness: MFTF ships with a huge library of ready-made action groups and page sections for core flows, and it stays strictly inside Magento’s tooling. If that matters to your team, it is still a defensible choice. For the work I do, none of it outweighed Playwright’s speed, TypeScript specs and trace viewer.

Making it reusable

One extra step paid off across projects: I moved the shared pieces — page objects, fixtures, the API helper, a base config and a CI workflow — into a single internal package that every store installs, so a fix in one place reaches all of them. That reusable shape is inspired by elgentos' magento2-bdd-e2e-testing-suite (MIT) — a great open-source Playwright suite for Magento 2 and Hyvä. If you are testing a single store and want something ready-made, start there.

Frequently asked questions

Is Playwright a good alternative to MFTF for Magento 2?
For most storefront and functional testing, yes. Playwright runs faster, is far easier to debug thanks to its trace viewer, and its TypeScript tests are less brittle than MFTF’s XML-and-Selenium approach. MFTF still makes sense if you lean heavily on its built-in action groups or need to stay strictly within Magento’s own tooling.
Can you use Playwright to test a Magento 2 store?
Absolutely. Playwright drives a real browser against your Magento storefront like any other site, and you can seed and clean up test data through the Magento REST API so each test runs in isolation. It works with both Luma and Hyvä themes.
Is Playwright faster than MFTF?
In my experience, clearly. Playwright runs tests in parallel out of the box and talks to Chromium directly instead of going through the Selenium/WebDriver stack, so wall-clock time on the suites I migrated dropped sharply — enough to run end-to-end tests on every push rather than only nightly.
Do I have to give up MFTF's data fixtures?
No — you keep the same idea. Instead of MFTF data entities, each Playwright test creates the customers or products it needs via the Magento REST API in a fixture and deletes them afterwards, so nothing is left in the database.