algoritma/shopware-test-utils

A collection of helpers for Shopware 6 integration/functional tests.

Installs: 13

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/algoritma/shopware-test-utils

dev-main 2025-12-23 16:40 UTC

This package is auto-updated.

Last update: 2025-12-23 16:40:43 UTC


README

A comprehensive collection of helpers, factories, and utilities for Shopware 6 integration and functional tests.

PHP Version Shopware Version License

๐Ÿ“ฆ Installation

composer require --dev algoritma/shopware-test-utils

๐ŸŽฏ Overview

This library provides a structured, clean, and maintainable approach to writing tests for Shopware 6 projects. It follows strict Single Responsibility Principle (SRP) and the Factory/Helper/Trait pattern to ensure separation of concerns.

Core Philosophy

"Factory CREATES, Helper ACTS, Trait ASSERTS"

  • Factories โ†’ Create and configure entities (Products, Orders, Customers, etc.)
  • Helpers โ†’ Execute actions on existing entities (place order, cancel, transition states, configuration, time travel)
  • Traits โ†’ Provide test assertions (database checks, event verification, mail assertions)

๐Ÿš€ Quick Start

Basic Integration Test

<?php

use Algoritma\ShopwareTestUtils\Core\AbstractIntegrationTestCase;
use Algoritma\ShopwareTestUtils\Factory\ProductFactory;
use Algoritma\ShopwareTestUtils\Factory\CustomerFactory;

class OrderPlacementTest extends AbstractIntegrationTestCase
{
    public function testUserCanPlaceOrder(): void
    {
        // Create test entities using Factories
        $customer = (new CustomerFactory($this->getContainer()))
            ->withEmail('test@example.com')
            ->withFirstName('John')
            ->withLastName('Doe')
            ->create();

        $product = (new ProductFactory($this->getContainer()))
            ->withName('Test Product')
            ->withPrice(19.99)
            ->withStock(100)
            ->create();

        $context = $this->createAuthenticatedContext($customer);

        // Create cart and add product
        $cart = $this->createCart($context)
            ->withProduct($product->getId())
            ->create();

        // Place order using Helper
        $order = $this->placeOrder($cart, $context);

        // Assert order was created
        $this->assertOrderState($order, 'open');
        $this->assertDatabaseHas('order', ['id' => $order->getId()]);
    }
}

Functional/Storefront Test

<?php

use Algoritma\ShopwareTestUtils\Core\AbstractFunctionalTestCase;

class StorefrontCheckoutTest extends AbstractFunctionalTestCase
{
    public function testCheckoutFlow(): void
    {
        // Create storefront request helper
        $storefront = $this->createStorefrontHelper();

        // Simulate user actions
        $storefront->addProductToCart($productId);
        $storefront->goToCheckout();
        $response = $storefront->submitOrder();

        $this->assertResponseIsSuccessful($response);
        $this->assertMailSent('order_confirmation');
    }
}

๐Ÿ“š Available Components

๐Ÿญ Factories (Create Entities)

Factories use the Builder pattern to create and configure entities with a fluent API.

Factory Description
ProductFactory Create products with variants, prices, and stock
CustomerFactory Create customers with addresses and groups
OrderFactory Create orders with line items and states
CategoryFactory Create category trees
CartFactory Build carts with products and promotions
MediaFactory Create media files (images, documents)
PromotionFactory Create promotions and discounts
RuleFactory Create business rules
SalesChannelFactory Create sales channels
ShippingMethodFactory Create shipping methods
PaymentMethodFactory Create payment methods
TaxFactory Create tax configurations

Example:

$product = (new ProductFactory($container))
    ->withName('Gaming Laptop')
    ->withPrice(1499.99)
    ->withStock(50)
    ->withTax(19.0)
    ->active()
    ->create();

๐Ÿ”ง Helpers (Execute Actions)

Helpers perform operations on existing entities. Use the HelperAccessor trait for easy access in tests.

Helper Description
OrderHelper Cancel orders, mark as paid/shipped, get order details
CartHelper Clear cart, remove items, recalculate
MediaHelper Assign media to products, delete media
StateManager Transition state machine states
CheckoutRunner Execute complete checkout flows
StorefrontRequestHelper Simulate storefront HTTP requests
MigrationDataTester Test data integrity in migrations
ConfigHelper Manage system configuration and feature flags
TimeHelper Time travel and date manipulation
ProductHelper Create and manage products
CustomerHelper Create and manage customers
SalesChannelHelper Create and manage sales channels
MailHelper Send and verify emails

Example:

use Algoritma\ShopwareTestUtils\Traits\HelperAccessor;

class MyTest extends AbstractIntegrationTestCase
{
    use HelperAccessor;

    public function testOrderFlow(): void
    {
        // Access helpers easily via trait methods
        $this->orderHelper()->markOrderAsPaid($orderId);
        $this->orderHelper()->markOrderAsShipped($orderId);

        // Set configuration
        $this->configHelper()->set('core.cart.maxQuantity', 100);

        // Time travel
        $this->timeHelper()->travelForward('30 days');
    }
}

โœจ Traits (Assertion Helpers)

Traits provide assertion methods for test verification. Note: Actions have been moved to Helper classes.

Trait Description
HelperAccessor Provides easy access to all Helper classes
DatabaseHelpers Database assertions (table exists, row count, etc.)
CacheHelpers Cache assertions (key exists, cache cleared)
TimeHelpers Time-related assertions (date in future/past, timestamp validity)
LogHelpers Log assertions (error logged, warning count, log contains)
MailHelpers Mail assertions (email sent, recipient correct)
EventHelpers Event assertions (event dispatched, payload validation)
QueueHelpers Queue assertions (job queued, queue empty)
MigrationHelpers Migration assertions (idempotency, schema changes)

Example:

use Algoritma\ShopwareTestUtils\Traits\HelperAccessor;
use Algoritma\ShopwareTestUtils\Traits\TimeHelpers;
use Algoritma\ShopwareTestUtils\Traits\MailHelpers;

class SubscriptionTest extends AbstractIntegrationTestCase
{
    use HelperAccessor;  // Access to all helpers
    use TimeHelpers;     // Time assertions
    use MailHelpers;     // Mail assertions

    public function testSubscriptionRenewal(): void
    {
        // Use TimeHelper for actions
        $this->timeHelper()->freezeTime(new \DateTime('2025-01-01 00:00:00'));

        // ... create subscription ...

        // Travel forward 30 days
        $this->timeHelper()->travelForward('30 days');

        // Run renewal process
        $this->runScheduledTask(RenewalTask::class);

        // Use MailHelpers trait for assertions
        $this->assertMailSent(1);
        $this->assertMailWasSent();
    }
}

๐Ÿงช Test Base Classes

AbstractIntegrationTestCase

Base class for integration tests (database, repositories, services).

Features:

  • Database transaction rollback after each test
  • Access to Shopware container and services
  • Event capturing
  • Mail capturing
  • Queue testing support
  • Custom assertions
  • Fixture Loading: Load fixtures with automatic dependency resolution and container injection.

AbstractFunctionalTestCase

Base class for functional/storefront tests (HTTP requests, controllers).

Extends: AbstractIntegrationTestCase

Additional Features:

  • Storefront browser simulation
  • HTTP request/response testing
  • Session management

MigrationTestCase

Base class for migration tests.

Features:

  • Test migration up/down
  • Assert table creation/modification
  • Test data integrity
  • Assert migration idempotency

๐ŸŽจ Custom Assertions

The ShopwareAssertions trait provides Shopware-specific assertions:

// Entity assertions
$this->assertEntityExists('product', $productId);

// Price assertions
$this->assertPriceEquals(19.99, $product);

// Customer assertions
$this->assertCustomerHasRole($customer, 'B2B');

// Database assertions
$this->assertDatabaseHas('product', ['id' => $productId, 'active' => 1]);
$this->assertDatabaseMissing('order', ['id' => $deletedOrderId]);

// Order assertions
$this->assertOrderState($order, 'completed');

// Rule assertions
$this->assertRuleMatches($ruleId, $salesChannelContext);

// State machine assertions
$this->assertStateMachineState($orderId, 'order_state', 'completed');

// Table structure assertions
$this->assertTableExists('custom_table');
$this->assertColumnExists('product', 'custom_field');
$this->assertIndexExists('product', 'idx_custom');
$this->assertForeignKeyExists('order_line_item', 'fk_order_id');

// Mail Template assertions
$this->assertMailTemplateExists('order_confirmation');
$this->assertMailTemplateSubjectContains('order_confirmation', 'en-GB', 'Order confirmation');
$this->assertMailTemplateContentContains('order_confirmation', 'en-GB', 'Thank you for your order', true);

๐Ÿ—‚๏ธ Directory Structure

src/
โ”œโ”€โ”€ Assert/
โ”‚   โ””โ”€โ”€ ShopwareAssertions.php          # Custom assertions
โ”œโ”€โ”€ Core/
โ”‚   โ”œโ”€โ”€ AbstractIntegrationTestCase.php # Integration test base
โ”‚   โ”œโ”€โ”€ AbstractFunctionalTestCase.php  # Functional test base
โ”‚   โ””โ”€โ”€ MigrationTestCase.php           # Migration test base
โ”œโ”€โ”€ Factory/                             # Entity factories
โ”‚   โ”œโ”€โ”€ ProductFactory.php
โ”‚   โ”œโ”€โ”€ CustomerFactory.php
โ”‚   โ”œโ”€โ”€ OrderFactory.php
โ”‚   โ”œโ”€โ”€ CartFactory.php
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ Helper/                              # Action helpers
โ”‚   โ”œโ”€โ”€ OrderHelper.php
โ”‚   โ”œโ”€โ”€ CartHelper.php
โ”‚   โ”œโ”€โ”€ MediaHelper.php
โ”‚   โ”œโ”€โ”€ StateManager.php
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ Traits/                              # Reusable behaviors
โ”‚   โ”œโ”€โ”€ DatabaseHelpers.php
โ”‚   โ”œโ”€โ”€ CacheHelpers.php
โ”‚   โ”œโ”€โ”€ TimeHelpers.php
โ”‚   โ”œโ”€โ”€ EventHelpers.php
โ”‚   โ””โ”€โ”€ ...
โ””โ”€โ”€ Fixture/
    โ”œโ”€โ”€ FixtureInterface.php
    โ”œโ”€โ”€ FixtureManager.php
    โ”œโ”€โ”€ AbstractFixture.php
    โ””โ”€โ”€ ReferenceRepository.php

๐Ÿ” Advanced Examples

Testing with Fixtures

use Algoritma\ShopwareTestUtils\Fixture\AbstractFixture;
use Algoritma\ShopwareTestUtils\Fixture\ReferenceRepository;

class MyFixture extends AbstractFixture
{
    public function load(ReferenceRepository $references): void
    {
        // Access container
        $repo = $this->getContainer()->get('product.repository');
        
        // Create data...
    }
}

class MyTest extends AbstractIntegrationTestCase
{
    public function testWithFixture(): void
    {
        $this->loadFixtures(new MyFixture());
        
        // ...
    }
}

Testing with Time Travel

use Algoritma\ShopwareTestUtils\Traits\TimeHelpers;

class CouponExpirationTest extends AbstractIntegrationTestCase
{
    use TimeHelpers;

    public function testCouponExpires(): void
    {
        $coupon = $this->createCoupon(['validUntil' => '2025-12-31']);

        // Test before expiration
        $this->freezeTime(new \DateTime('2025-06-01'));
        $this->assertTrue($coupon->isValid());

        // Test after expiration
        $this->travelTo(new \DateTime('2026-01-01'));
        $this->assertFalse($coupon->isValid());
    }
}

Testing with Event Capture

use Algoritma\ShopwareTestUtils\Traits\EventHelpers;

class ProductEventTest extends AbstractIntegrationTestCase
{
    use EventHelpers;

    public function testProductCreationDispatchesEvent(): void
    {
        $this->startCapturingEvents();

        $product = (new ProductFactory($this->getContainer()))
            ->create();

        $this->assertEventWasDispatched(ProductCreatedEvent::class);
        $event = $this->getDispatchedEvent(ProductCreatedEvent::class);
        $this->assertEquals($product->getId(), $event->getProductId());
    }
}

Testing Migrations

use Algoritma\ShopwareTestUtils\Core\MigrationTestCase;
use Algoritma\ShopwareTestUtils\Traits\MigrationHelpers;

class Migration1234567890Test extends MigrationTestCase
{
    use MigrationHelpers;

    public function testMigrationCreatesTable(): void
    {
        // Test idempotency (can run multiple times)
        $this->assertMigrationIsIdempotent(Migration1234567890::class);

        // Test table creation
        $this->assertMigrationAddsTable(Migration1234567890::class, 'custom_entity');

        // Test column exists
        $this->assertColumnExists('custom_entity', 'custom_field');
    }

    public function testDataIntegrity(): void
    {
        // Seed old data
        $this->seedTable('old_table', [
            ['id' => 1, 'name' => 'Test']
        ]);

        // Run migration
        $this->runMigration(Migration1234567890::class);

        // Test data was migrated correctly
        $this->assertDatabaseHas('new_table', ['id' => 1, 'name' => 'Test']);
    }
}

Testing with Database Snapshots

use Algoritma\ShopwareTestUtils\Traits\DatabaseHelpers;

class BulkOperationTest extends AbstractIntegrationTestCase
{
    use DatabaseHelpers;

    public function testBulkImport(): void
    {
        // Create snapshot before bulk operation
        $snapshotId = $this->snapshotTable('product');

        // Perform bulk import
        $this->importProducts($csvFile);

        // Test the import
        $this->assertDatabaseHas('product', ['sku' => 'NEW-SKU-001']);

        // Restore original state for other tests
        $this->restoreTableSnapshot($snapshotId);
    }
}

๐Ÿ› ๏ธ Configuration

PHPUnit Configuration

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/12.0/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true">
    <testsuites>
        <testsuite name="Integration">
            <directory>tests/Integration</directory>
        </testsuite>
        <testsuite name="Functional">
            <directory>tests/Functional</directory>
        </testsuite>
    </testsuites>
</phpunit>

Running Tests

# Run all tests
vendor/bin/phpunit

# Run only integration tests
vendor/bin/phpunit --testsuite Integration

# Run with coverage
vendor/bin/phpunit --coverage-html coverage/

๐Ÿ“– Documentation

For detailed architecture documentation, see ARCHITECTURE.md.

๐Ÿค Contributing

Contributions are welcome! Please follow these guidelines:

  1. Factories should only create entities
  2. Helpers should only perform actions
  3. Traits should provide reusable behaviors
  4. All code must follow PSR-12 coding standards
  5. Add tests for new features

Code Quality Tools

# Check code style
composer cs-check

# Fix code style
composer cs-fix

# Run static analysis
composer phpstan

# Run rector
composer rector-check

๐Ÿ“„ License

This project is licensed under the MIT License.

๐Ÿ™ Credits

Developed by Algoritma for the Shopware community.

๐Ÿ’ก Support

For issues, questions, or feature requests, please open an issue on GitHub.