Services
The test suite provides several services that can be used to simplify your test code. These services are designed to be reusable and can be easily extended to fit your specific needs.
Test Data Service
The TestDataService
is a powerful utility designed to simplify test data creation, management, and cleanup when writing acceptance and API tests for Shopware. It provides ready-to-use functions for common data needs and ensures reliable, isolated test environments. For detailed documentation of the methods, you can have a look at the service class or use the auto-completion of your IDE.
When to use the TestDataService in tests
You should use the TestDataService
whenever you need test data that matches common Shopware structures, such as:
- Creating a basic product, customer, order, category, etc
- Setting up media resources like product images or digital downloads
- Creating promotions, rules, or payment/shipping methods
- Fetching existing entities via helper methods (
getCurrency()
,getShippingMethod()
, etc) - Assigning relations between entities (e.g., linking a product to a category)
Typical examples
const product = await TestDataService.createBasicProduct();
const customer = await TestDataService.createCustomer();
const shipping = await TestDataService.createBasicShippingMethod();
When and why to extend the TestDataService
You should add new functions to the TestDataService
(or extend it) when:
- Your project or plugin introduces new entity types (e.g.,
CommercialCustomerGroup
,CustomProductType
) - You need a specialized creation logic (e.g., a shipping method with multiple rules, a pre-configured product bundle)
- Existing methods require modifications that should not affect the core service
- You want to reuse the same setup across multiple tests without duplicating logic
- You require special cleanup handling for newly created entities
Using and extending the TestDataService
properly ensures your acceptance tests stay readable, maintainable, and scalable even as your Shopware project grows.
Available create*
methods in TestDataService
These methods are designed to streamline the setup of test data, ensuring consistency and efficiency in your testing processes. They are much more available than listed below, but these are the most common ones. Use your IDE auto-completion to find all available methods.
createBasicProduct(): Promise<Product>
createVariantProducts(parentProduct: Product, propertyGroups: PropertyGroup[]): Promise<Product[]>
createCustomer(): Promise<Customer>
createCustomerGroup(): Promise<CustomerGroup>
createOrder(lineItems: SimpleLineItem[], customer: Customer): Promise<Order>
createCategory(): Promise<Category>
createColorPropertyGroup(): Promise<PropertyGroup>
createBasicPaymentMethod(): Promise<PaymentMethod>
createBasicShippingMethod(): Promise<ShippingMethod>
- [...]
Available assign*
methods in TestDataService
These methods are designed to establish associations between entities, such as linking products to categories or assigning media to manufacturers, ensuring that your test data reflects realistic scenarios. They are much more available than listed below, but these are the most common ones. Use your IDE auto-completion to find all available methods.
assignProductCategory(productId: string, categoryIds: string[]): Promise<void>
assignProductManufacturer(productId: string, manufacturerId: string): Promise<void>
assignProductMedia(productId: string, mediaId: string): Promise<void>
- [...]
Available get*
methods in TestDataService
They are much more available than listed below, but these are the most common ones. Use your IDE auto-completion to find all available methods.
getCountry(iso2: string): Promise<Country>
getCurrency(isoCode: string): Promise<Currency>
getCustomerGroups(): Promise<CustomerGroup[]>
getPaymentMethod(name = 'Invoice'): Promise<PaymentMethod>
- [...]
Writing new methods in TestDataService
If you want to add new functionality to this service such as a new type of entity creation, you can follow this approach:
1. Define the purpose
Decide whether you're creating, assigning, or retrieving data. Most methods fall into one of the following patterns:
create*
: Creates a new entity (e.g., product, customer, category)assign*
: Links existing entities (e.g., assign media to product)get*
: Retrieves specific or filtered data from the system
2. Implement the method
Use the AdminApiContext
to interact with the Shopware Admin API. Here is a simplified example of adding a method to create a new shipping method
3. Follow naming conventions
Be consistent in naming:
- Use
createBasic*
for standardized, default setups with predefined values (e.g.,createBasicProduct
) - Use
create*With*
for variations (e.g.createProductWithImage
) - Use
assign*
for methods that associate two entities (e.g.,assignProductMedia
) - Use
get*
to retrieve specific entities or lists (e.g.getCurrency
)
4. Add a return type
Always define a return type (typically a Promise<...>
) to improve autocompletion and documentation support.
5. Add cleanup logic
Make sure to clean up the entity via code after the test run by putting the entity in a record. See the example below:
async createBasicRule(): Promise<Rule> {
[...]
this.addCreatedRecord('rule', rule.id);
[...]
}
Explore further info on this in Automatic Cleanup.
6. Test the method
Once added, use your new method inside a test to verify it works as expected (/tests/TestDataService.spec.ts
):
test('Verify new shipping method creation', async ({ TestDataService }) => {
const shippingMethod = await TestDataService.createShippingMethod({
name: 'Express Delivery'
});
expect(shippingMethod.name).toEqual('Express Delivery');
});
Automatic cleanup of test data and system configurations
The TestDataService
includes a built-in mechanism to ensure that any test data & system configuration entries created during a test run are automatically deleted afterward. This ensures that the Shopware instance remains clean and consistent between tests, helping to maintain test isolation and prevent state leakage.
How cleanup works
When you create an entity using a create*
method (e.g., createBasicProduct
, createCustomer
), the service automatically registers that entity for deletion by calling the addCreatedRecord()
method:
this.addCreatedRecord('product', product.id);
These records are stored in a cleanup queue processed at the end of each test using the Playwright lifecycle.
Cleanup execution
The cleanup()
method handles the deletion of all registered entities and system config changes. All created records are grouped into two categories:
- Priority Deletions (
priorityDeleteOperations
) – for entities with dependencies that must be deleted first (e.g., orders, customers) - Standard Deletions (
deleteOperations
) – for all other entities
This prioritization prevents errors when deleting interdependent data. Any modified system configurations are reset to their previous state after deleting priority records. The priority entities can be found in the TestDataService
class. If you want to add a new entity to the priority deletion list, you can do so by adding it to the priorityDeleteOperations
array.
Skipping cleanup
In rare scenarios, such as performance testing or debugging, you may want to prevent cleanup for specific entities. You can simply skip the cleanup by calling TestDataService.setCleanUp(false)
within your test.
Extending the TestDataService in external projects
The TestDataService
is designed to be easily extendable. This allows you to add project-specific data generation methods while still benefiting from the existing, standardized base functionality.
1. Create a new subclass
You can create a new TypeScript class that extends the base TestDataService
.
import { TestDataService } from '@shopware-ag/acceptance-test-suite';
export class CustomTestDataService extends TestDataService {
constructor(AdminApiContext, DefaultSalesChannel) {
super(...);
}
async createCustomCustomerGroup(data: Partial<CustomerGroup>) {
const response = await this.adminApi.post('customer-group?_response=true', {
data: {
...
},
});
const { data: createdGroup } = await response.json();
this.addCreatedRecord('customer-group', createdGroup.id);
return createdGroup;
}
}
2. Provide the extended service as a fixture
Following the Playwright fixture system described, you create a new fixture that initializes your extended service.
Example from AcceptanceTest.ts
:
import { test as base } from '@shopware-ag/acceptance-test-suite';
import type { FixtureTypes } from './BaseTestFile';
import { CustomTestDataService } from './CustomTestDataService';
export interface CustomTestDataServiceType {
TestDataService: CustomTestDataService;
}
export const test = base.extend<FixtureTypes & CustomTestDataServiceType>({
TestDataService: async ({ AdminApiContext, DefaultSalesChannel }, use) => {
const service = new CustomTestDataService(AdminApiContext, DefaultSalesChannel.salesChannel);
await use(service);
await service.cleanUp();
},
});
In this setup:
- The
TestDataService
fixture is overridden with your customCustomTestDataService
. - Now, all tests that use
TestDataService
will have access to both the original and your extended methods. - The automated cleanup is still in place, ensuring that any test data created during the test run is removed afterward.