Edit in GitHubLog an issue

Working with data fixtures

Data fixtures are reusable components that set up test data in your database before running integration tests. They help you create a consistent, predictable test environment by populating the database with entities like products, customers, orders, categories, and more.

Think of data fixtures as the "arrange" phase of your test--they prepare the data your test needs to verify specific functionality.

Modern vs legacy approaches

Adobe Commerce and Magento Open Source support multiple ways to work with data fixtures, but not all are equally recommended.

Comparison

ApproachStatusCan ParameterizeSyntax Example
Legacy fixtures (file-based)
Deprecated
❌ No
@magentoDataFixture Magento/Catalog/_files/product.php
Parameterized data fixtures (with PHP Attributes)
✅ Recommended
✅ Yes
#[DataFixture(ProductFixture::class, ['price' => 10], 'product')]

Why avoid legacy fixtures?

  • No parameterization - Each variation requires a separate file
  • Hard to maintain - Changes require modifying PHP scripts scattered across the codebase
  • Poor discoverability - Difficult to find what data a fixture creates
  • No type safety - Parameters and return values are not typed
  • Coupling - Tests become tightly coupled to specific fixture files

Types of data fixtures

This section covers the two main types of data fixtures: legacy file-based fixtures and modern parameterized data fixtures.

These are PHP classes that implement DataFixtureInterface or RevertibleDataFixtureInterface. They support powerful parameterization and require PHP Attributes (#[DataFixture()]).

The power of modern parameterized data fixtures lies in their ability to be customized as you can configure the data they create without writing new fixture classes.

For detailed information on basic parameterization, aliases, store scope, and the count parameter, see DataFixture Attribute.

Basic example:

Copied to your clipboard
// Only override values that matter for your test. SKU is auto-generated with a unique value
#[
DataFixture(ProductFixture::class, ['price' => 10.00], 'product')
]
public function testProductExists(): void
{
$fixtures = DataFixtureStorageManager::getStorage();
$product = $fixtures->get('product');
}

Fixture references - building complex scenarios:

You can reference data from one fixture in another using the $alias.property$ syntax. This allows you to build complex, interconnected test data scenarios:

Copied to your clipboard
#[
DataFixture(CustomerFixture::class, as: 'customer'),
DataFixture(User::class, as: 'user'),
DataFixture(
Company::class,
['sales_representative_id' => '$user.id$', 'super_user_id' => '$customer.id$'],
'company'
)
]

Legacy fixtures (Deprecated)

Legacy fixtures are file-based PHP scripts (stored in _files/ directories) that directly execute database operations. They cannot be parameterized and are now deprecated.

Example with DocBlock annotation:

Copied to your clipboard
/**
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
*/
public function testProductExists(): void
{
// Test logic here - but you're stuck with whatever data the file creates
}

Example of a legacy fixture file: Magento/Catalog/_files/product_simple.php

Copied to your clipboard
<?php
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\TestFramework\Helper\Bootstrap;
$product = Bootstrap::getObjectManager()->create(Product::class);
$product->setTypeId('simple')
->setAttributeSetId(4)
->setName('Simple Product')
->setSku('simple') // Fixed SKU - can't customize
->setPrice(10) // Fixed price - can't customize
->setVisibility(4)
->setStatus(1);
Bootstrap::getObjectManager()
->get(ProductRepositoryInterface::class)
->save($product);

Problems: To create a product with a different SKU or price, you need to create an entirely new fixture file!

Migrating legacy fixtures to modern

If you have tests using legacy fixtures, you should migrate them to parameterized data fixtures with PHP Attributes. New legacy fixtures cannot be created.

Before (Legacy fixtures)

Copied to your clipboard
/**
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
*/
public function testProductPrice(): void
{
$product = $this->productRepository->get('simple'); // Fixed SKU
$this->assertEquals(10, $product->getPrice()); // Fixed price
}

After (Parameterized data fixtures with PHP Attributes)

Copied to your clipboard
#[
DataFixture(ProductFixture::class, ['price' => 10], 'product')
]
public function testProductPrice(): void
{
$fixtures = DataFixtureStorageManager::getStorage();
$product = $fixtures->get('product');
$this->assertEquals(10, $product->getPrice());
}

Retrieving fixture data

Always use DataFixtureStorageManager to retrieve fixture data created by fixtures:

Copied to your clipboard
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
#[
DataFixture(ProductFixture::class, ['price' => 25.00], 'test_product')
]
public function testProduct(): void
{
$product = DataFixtureStorageManager::getStorage()->get('test_product');
$this->assertEquals(25.00, $product->getPrice());
}

Complex example: Inventory with multiple sources

Example showing fixture composition and references:

Copied to your clipboard
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
#[
DataFixture(SourceFixture::class, ['source_code' => 'test_source'], 'source'),
DataFixture(StockFixture::class, as: 'stock'),
DataFixture(
StockSourceLinksFixture::class,
[['stock_id' => '$stock.stock_id$', 'source_code' => '$source.source_code$']]
),
DataFixture(
StockSalesChannelsFixture::class,
['stock_id' => '$stock.stock_id$', 'sales_channels' => ['base']]
),
DataFixture(ProductFixture::class, as: 'p1'),
DataFixture(ProductFixture::class, as: 'p2'),
DataFixture(
SourceItemsFixture::class,
[
['sku' => '$p1.sku$', 'source_code' => '$source.source_code$', 'quantity' => 10, 'status' => 1],
['sku' => '$p2.sku$', 'source_code' => '$source.source_code$', 'quantity' => 20, 'status' => 1],
]
),
DataFixture(Indexer::class, as: 'indexer')
]
public function testInventoryConfiguration(): void
{
$stock = DataFixtureStorageManager::getStorage()->get('stock');
$product1 = DataFixtureStorageManager::getStorage()->get('p1');
// Test inventory logic
}

This example demonstrates:

  • Creating related fixtures (source, stock, products)
  • Linking fixtures together using $alias.property$ references
  • Passing arrays of data with references
  • Building complex test scenarios with multiple dependencies

Best practices

Follow these best practices when working with data fixtures.

Do not create legacy fixtures

Bad - Cannot create new:

Copied to your clipboard
/**
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
*/

Good - Use parameterized data fixtures with PHP Attributes:

Copied to your clipboard
#[DataFixture(ProductFixture::class, ['price' => 10])]

Use PHP Attributes for parameterized data fixtures

Parameterized data fixtures require PHP Attributes.

Keep fixtures focused

Each fixture should do one thing. Use multiple fixtures and compose them rather than creating complex all-in-one fixtures.

Use fixture aliases for references

Always use the as parameter when you need to reference fixture data in other fixtures:

Copied to your clipboard
#[
DataFixture(CategoryFixture::class, as: 'category'),
DataFixture(ProductFixture::class, ['category_ids' => ['$category.id$']])
]

Leverage fixture defaults

Most fixtures have sensible defaults with dynamic unique values. Only override values that matter for your specific test scenario.

Don't override unique fields unnecessarily:

Copied to your clipboard
// Bad - overriding SKU when not needed
#[DataFixture(ProductFixture::class, ['sku' => 'my-sku', 'price' => 100], 'product')]
// Good - let SKU be auto-generated and reference it when needed
#[
DataFixture(ProductFixture::class, ['price' => 100], 'product'),
DataFixture(
CatalogRuleFixture::class,
[
'is_active' => false,
'conditions' => [['attribute' => 'sku', 'value' => '$product.sku$']]
],
'rule'
)
]
public function testCatalogRule(): void
{
$product = DataFixtureStorageManager::getStorage()->get('product');
$rule = DataFixtureStorageManager::getStorage()->get('rule');
// Access auto-generated SKU
$sku = $product->getSku();
}

You can reference auto-generated values using $alias.property$ syntax without overriding them.

Understand isolation behavior

Fixture cleanup is automatic

Fixtures that implement RevertibleDataFixtureInterface are automatically cleaned up. This is critical to prevent test pollution where data from one test affects another test.

Rely only on explicitly configured fixtures

Your tests should depend on fixtures created before the test runs. However, only rely on things you've explicitly configured in your test's fixture declarations, not on implicit side effects or fixtures from other tests.

Finding available fixtures

Most Commerce modules include test fixtures in their Test/Fixture directory:

  • Magento/Catalog/Test/Fixture/Product.php
  • Magento/Customer/Test/Fixture/Customer.php
  • Magento/Sales/Test/Fixture/Order.php
  • Magento/Cms/Test/Fixture/Page.php

Check these directories in the Adobe Commerce or Magento Open Source codebase to discover available fixtures and their parameters.

Creating custom fixtures

If you need custom fixtures, create a class that implements DataFixtureInterface or RevertibleDataFixtureInterface.

Basic structure

Copied to your clipboard
<?php
namespace Vendor\Module\Test\Fixture;
use Magento\TestFramework\Fixture\DataFixtureInterface;
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
class CustomFixture implements RevertibleDataFixtureInterface
{
public function __construct(
private YourDependency $dependency
) {}
public function apply(array $data = []): ?DataFixtureStorageInterface
{
// Create your entity
$entity = $this->dependency->create($data);
return new DataFixtureStorage($entity);
}
public function revert(DataFixtureInterface $dataFixture): void
{
// Clean up the entity
$entity = $dataFixture->get();
$this->dependency->delete($entity);
}
}

When to use RevertibleDataFixtureInterface

Use RevertibleDataFixtureInterface when the data fixture creates data that needs to be manually removed after the test. Examples:

  • Product data
  • Category data
  • Customer data
  • Any persistent entity

Use DataFixtureInterface when the data is automatically removed with related data. Examples:

  • Adding a shipping address to a cart (removed when cart is deleted)
  • Assigning a product to a category (removed when product is deleted)

Learn more

Refer to the following resources for more information on data fixtures.

Official Documentation

DevBlog Articles

  • Privacy
  • Terms of Use
  • Do not sell or share my personal information
  • AdChoices
Copyright © 2025 Adobe. All rights reserved.