what unit, integration and MFTF each deliver
The most common misallocation in test automation: too many slow end-to-end tests and too few fast unit tests. The test pyramid provides the right weighting, three levels with clearly separated responsibilities that together form a reliable test strategy.
Table of Contents
- 1. The concept of the test pyramid for Magento
- 2. Unit tests: the base of the pyramid
- 3. Integration tests: verifying how things work together
- 4. MFTF: end-to-end in the browser
- 5. The right ratio between test levels
- 6. Infrastructure overhead per test level
- 7. The test pyramid in the CI/CD pipeline
- 8. Anti-patterns: the inverted pyramid
- 9. Direct comparison of the three test levels
- 10. Summary
- 11. FAQ
1. The concept of the test pyramid for Magento
The test pyramid is a model that describes how tests should be distributed by speed, degree of isolation and infrastructure overhead. At the base sit many fast unit tests, in the middle fewer integration tests, at the top few end-to-end tests. For Magento 2 this means concretely: unit tests with PHPUnit and no bootstrap, integration tests with a full Magento bootstrap and database access, MFTF tests with browser automation against a complete Magento installation.
The model is not dogma, it is an orientation. In Magento projects that are heavily database driven, the emphasis shifts somewhat toward integration tests, because many core features can only be tested meaningfully with real database operations. Even so, the basic rule remains: fast, isolated tests dominate the base, slow, infrastructure-heavy tests stay at the top.
A common mistake in Magento projects is the absence of an explicit test strategy. Tests emerge ad hoc, usually as integration or MFTF tests, because they verify the visible outcome directly. The result is an inverted pyramid, many slow tests that take several minutes on every merge, and no unit tests that give feedback in seconds. The test pyramid provides the correct counterweight.
2. Unit tests: the base of the pyramid
Unit tests in Magento 2 run under dev/tests/unit/ with a minimal bootstrap that only activates the autoloader. All dependencies of the class under test are replaced with PHPUnit mocks. The result: a test suite with a hundred tests that completes in under five seconds, with no database connection, no Magento instance, no browser. This speed advantage is the main reason unit tests form the base of the pyramid.
What unit tests cover in Magento: the logic of view models, services and helpers, exception handling in repositories, calculation logic in pricing models, validation rules in data transfer objects. What they do not cover: database interactions, event dispatching with real observers, layout rendering, session handling. This clear boundary matters, anyone who ignores it and tries to mock database operations inside a unit test ends up building mocks that only pretend to be the real implementation.
<?php
// File: dev/tests/unit/phpunit.xml (relevant excerpt)
// Unit tests: no database, no full Magento bootstrap
// Run: vendor/bin/phpunit -c dev/tests/unit/phpunit.xml \
// --testsuite=Mironsoft_Catalog
// Typical unit test execution time: < 5 seconds for 100 tests
// Coverage: business logic, exception handling, calculation logic
// Integration test phpunit.xml is at: dev/tests/integration/phpunit.xml
// Integration tests require: running database, Magento install, fixtures
// Typical execution time: 30 seconds to several minutes
// MFTF: dev/tests/acceptance/
// Requires: full Magento stack, Selenium/WebDriver, browser
// Typical execution time: minutes per scenario
// The ratio in a healthy Magento project:
// Unit: 70% of all tests
// Integration: 25% of all tests
// MFTF/E2E: 5% of all tests (critical user journeys only)
3. Integration tests: verifying how things work together
Magento integration tests under dev/tests/integration/ use a full bootstrap with their own test database and an active DI container. They verify whether classes actually work together correctly in the real Magento context: whether a repository truly reads the right entries from the database, whether a plugin correctly modifies the method call, whether an observer produces the expected side effect after an event. This is the test level that surfaces things a unit test with mocks would hide.
Integration tests in Magento use fixtures, PHP scripts that write test data into the database and remove it again after the test. The @magentoDataFixture attribute references this fixture file and ensures the test runs against a defined database state. This mechanism makes integration tests repeatable and isolated from one another, even when they run against the same database. The downside: every database access costs time, which makes integration tests orders of magnitude slower than unit tests.
<?php
// File: app/code/Mironsoft/Catalog/Test/Integration/Model/ProductRepositoryTest.php
declare(strict_types=1);
namespace Mironsoft\Catalog\Test\Integration\Model;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Framework\Exception\NoSuchEntityException;
use Mironsoft\Catalog\Api\ProductRepositoryInterface;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
/**
* Integration test: uses real database, real DI container.
* Fixture creates test product before each test, rolls back after.
*/
class ProductRepositoryTest extends TestCase
{
private ProductRepositoryInterface $repository;
protected function setUp(): void
{
$objectManager = Bootstrap::getObjectManager();
$this->repository = $objectManager->get(ProductRepositoryInterface::class);
}
#[Test]
#[\Magento\TestFramework\Annotation\DataFixture(
'Mironsoft_Catalog::Test/Integration/_files/product_enabled.php'
)]
public function getByIdReturnsRealProductFromDatabase(): void
{
// This test actually queries the database, no mocks
$product = $this->repository->getById(1);
$this->assertSame('test-product-sku', $product->getSku());
$this->assertTrue((bool)$product->getStatus());
}
#[Test]
public function getByIdThrowsExceptionForNonExistentId(): void
{
$this->expectException(NoSuchEntityException::class);
$this->repository->getById(99999);
}
}
4. MFTF: end-to-end in the browser
The Magento Functional Testing Framework (MFTF) automates browser tests against a complete Magento installation. MFTF tests simulate real user interactions: adding a product to the cart, running through checkout, navigating the admin panel, filling out forms. They verify the overall system from the user's perspective and surface integration breaks that neither unit nor integration tests can find, for example when a JavaScript error disables the checkout button.
MFTF tests are written in XML and follow the page object pattern. Each page has an XML file that defines the selectors, and each test describes its steps as XML actions. That sounds like a lot of overhead, but it has one advantage: MFTF tests can be written and adjusted by QA staff without PHP knowledge. The downside is execution speed, a single MFTF test can take minutes because it waits for page loads, JavaScript execution and browser rendering.
5. The right ratio between test levels
A healthy test pyramid for a Magento custom module has roughly this ratio: 70% unit tests, 25% integration tests, 5% MFTF tests. In practice this means: every method with nontrivial logic gets a unit test, critical database operations get an integration test, and the most important user journeys, checkout, login, product search, get an MFTF test.
This ratio is not an absolute law. A module that primarily performs data migration needs more integration tests than a module that only implements price calculations. A module that drives complex frontend interactions needs more MFTF tests than a pure backend module. The test pyramid is the goal, the actual weighting depends on the module type. What always holds: MFTF tests are the most expensive to build and maintain and should remain the exception.
6. Infrastructure overhead per test level
Unit tests only need PHP, Composer and PHPUnit, they run in any environment with a PHP interpreter. Integration tests need a full Magento database (typically a separate test database) and a Magento install with caches disabled. MFTF tests additionally need a browser driver (Chrome plus ChromeDriver or Selenium Grid), a complete Magento installation with an active frontend, and MFTF configuration in .env.testing.
In a Docker-based development environment like Mark Shust's setup, unit tests and integration tests run directly inside the PHP container. MFTF tests require additional containers for Selenium. That noticeably raises the infrastructure overhead, both locally and in the CI pipeline. This overhead only pays off for the most critical end-to-end scenarios.
7. The test pyramid in the CI/CD pipeline
In a well configured CI pipeline, the three test levels run in stages: unit tests run on every commit and give feedback within seconds. Integration tests run on every pull request merge against the main branch and give feedback within minutes. MFTF tests run once daily or before a release and give feedback within ten to thirty minutes. This staging ensures developers get fast feedback for everyday changes while the expensive tests do not slow down the development flow.
A typical GitHub Actions configuration for Magento has separate jobs for the three test levels. The unit test job runs in parallel with linting and PHPStan. The integration test job only starts once the unit test job has succeeded, acting as a gate. The MFTF job only runs on the main branch or on release branches. This setup gives developers productive feedback every day without blocking the CI pipeline with long wait times.
8. Anti-patterns: the inverted pyramid
The most common anti-pattern in Magento projects is the inverted pyramid: many MFTF tests, few integration tests, no unit tests. This often arises because MFTF tests verify visible outcomes and are easy to understand, the tester sees the video of the bot navigating through checkout. Unit tests, by contrast, test abstract logic and require a good understanding of the classes involved. The price of the inverted pyramid is a CI pipeline that runs for thirty minutes and fails false-negative on every flaky MFTF test.
A second anti-pattern is excessive use of integration tests for logic that could be covered with unit tests. When a developer verifies every service with an integration test because they do not want to deal with mocking, the result is slow tests that fail on database errors without any business logic having changed. That undermines trust in the test suite.
| Criterion | Unit test | Integration test | MFTF |
|---|---|---|---|
| Execution time | Milliseconds | Seconds | Minutes |
| Infrastructure needs | Only PHP + PHPUnit | DB + Magento | Browser + full stack |
| Flakiness risk | Very low | Medium | High |
| Scope covered | Logic of a class | Module + database | Entire system |
| Maintenance effort | Low | Medium | High |
10. Summary
The Magento 2 test pyramid describes how unit tests, integration tests and MFTF tests relate to each other and what task each level performs. Unit tests form the broad base: fast, isolated, requiring no infrastructure. Integration tests verify how things work together with the database and the DI container. MFTF tests verify critical user journeys in the browser. The optimal ratio is 70% unit, 25% integration, 5% MFTF, with shifts depending on the module type.
Anyone who inverts the pyramid and writes mainly MFTF tests pays with long CI runtimes, high maintenance effort and flaky tests. Anyone who builds it correctly gets fast feedback on every commit, reliable integration verification on every merge, and end-to-end assurance for the most important processes.
Magento 2 Test Pyramid, the essentials at a glance
Unit tests
Base of the pyramid. dev/tests/unit/ without DB. Milliseconds per test. Logic, exception handling, calculations. 70% of tests.
Integration tests
Middle of the pyramid. Real DB, full Magento bootstrap. Fixtures. Database queries, plugin behavior, events. 25% of tests.
MFTF
Top of the pyramid. Browser + full stack. Minutes per test. Critical user journeys. 5% of tests, only for the essentials.
CI staging
Unit on every commit. Integration on every PR merge. MFTF daily or before release. Staged feedback, minimal wait time.
Mironsoft
Magento 2 test strategy, PHPUnit and MFTF automation
Want to build a test strategy for your Magento project?
We analyze existing test gaps, build a test pyramid tailored to your project structure, and integrate all three test levels into the CI/CD pipeline.
Strategy audit
Analysis of the existing test landscape and recommendations for the right pyramid
Test implementation
Building unit, integration and MFTF tests for critical Magento modules
CI pipeline
Setting up staged test jobs in GitHub Actions or GitLab CI