Ecotone
SponsorBlogGithubSupport and ContactCommunity Channel
  • About
  • Installation
  • How to use
    • CQRS PHP
    • Event Handling PHP
    • Aggregates & Sagas
    • Scheduling in PHP
    • Asynchronous PHP
    • Event Sourcing PHP
    • Microservices PHP
    • Resiliency and Error Handling
    • Laravel Demos
    • Symfony Demos
      • Doctrine ORM
  • Tutorial
    • Before we start tutorial
    • Lesson 1: Messaging Concepts
    • Lesson 2: Tactical DDD
    • Lesson 3: Converters
    • Lesson 4: Metadata and Method Invocation
    • Lesson 5: Interceptors
    • Lesson 6: Asynchronous Handling
  • Enterprise
  • Modelling
    • Introduction
    • Message Bus and CQRS
      • CQRS Introduction - Commands
        • Query Handling
        • Event Handling
      • Aggregate Introduction
        • Aggregate Command Handlers
        • Aggregate Query Handlers
        • Aggregate Event Handlers
        • Advanced Aggregate creation
      • Repositories Introduction
      • Business Interface
        • Introduction
        • Business Repository
        • Database Business Interface
          • Converting Parameters
          • Converting Results
      • Saga Introduction
      • Identifier Mapping
    • Extending Messaging (Middlewares)
      • Message Headers
      • Interceptors (Middlewares)
        • Additional Scenarios
      • Intercepting Asynchronous Endpoints
      • Extending Message Buses (Gateways)
    • Event Sourcing
      • Installation
      • Event Sourcing Introduction
        • Working with Event Streams
        • Event Sourcing Aggregates
          • Working with Aggregates
          • Applying Events
          • Different ways to Record Events
        • Working with Metadata
        • Event versioning
        • Event Stream Persistence
          • Event Sourcing Repository
          • Making Stream immune to changes
          • Snapshoting
          • Persistence Strategies
          • Event Serialization and PII Data (GDPR)
      • Projection Introduction
        • Configuration
        • Choosing Event Streams for Projection
        • Executing and Managing
          • Running Projections
          • Projection CLI Actions
          • Access Event Store
        • Projections with State
        • Emitting events
    • Recovering, Tracing and Monitoring
      • Resiliency
        • Retries
        • Error Channel and Dead Letter
          • Dbal Dead Letter
        • Idempotent Consumer (Deduplication)
        • Resilient Sending
        • Outbox Pattern
        • Concurrency Handling
      • Message Handling Isolation
      • Ecotone Pulse (Service Dashboard)
    • Asynchronous Handling and Scheduling
      • Asynchronous Message Handlers
      • Asynchronous Message Bus (Gateways)
      • Delaying Messages
      • Time to Live
      • Message Priority
      • Scheduling
      • Dynamic Message Channels
    • Distributed Bus and Microservices
      • Distributed Bus
        • Distributed Bus with Service Map
          • Configuration
          • Custom Features
          • Non-Ecotone Application integration
          • Testing
        • AMQP Distributed Bus (RabbitMQ)
          • Configuration
        • Distributed Bus Interface
      • Message Consumer
      • Message Publisher
    • Business Workflows
      • The Basics - Stateless Workflows
      • Stateful Workflows - Saga
      • Handling Failures
    • Testing Support
      • Testing Messaging
      • Testing Aggregates and Sagas with Message Flows
      • Testing Event Sourcing Applications
      • Testing Asynchronous Messaging
  • Messaging and Ecotone In Depth
    • Overview
    • Multi-Tenancy Support
      • Getting Started
        • Any Framework Configuration
        • Symfony and Doctrine ORM
        • Laravel
      • Different Scenarios
        • Hooking into Tenant Switch
        • Shared and Multi Database Tenants
        • Accessing Current Tenant in Message Handler
        • Events and Tenant Propagation
        • Multi-Tenant aware Dead Letter
      • Advanced Queuing Strategies
    • Document Store
    • Console Commands
    • Messaging concepts
      • Message
      • Message Channel
      • Message Endpoints/Handlers
        • Internal Message Handler
        • Message Router
        • Splitter
      • Consumer
      • Messaging Gateway
      • Inbound/Outbound Channel Adapter
    • Method Invocation And Conversion
      • Method Invocation
      • Conversion
        • Payload Conversion
        • Headers Conversion
    • Service (Application) Configuration
    • Contributing to Ecotone
      • How Ecotone works under the hood
      • Ecotone Phases
      • Registering new Module Package
      • Demo Integration with SQS
        • Preparation
        • Inbound and Outbound Adapters and Message Channel
        • Message Consumer and Publisher
  • Modules
    • Overview
    • Symfony
      • Symfony Configuration
      • Symfony Database Connection (DBAL Module)
      • Doctrine ORM
      • Symfony Messenger Transport
    • Laravel
      • Laravel Configuration
      • Database Connection (DBAL Module)
      • Eloquent
      • Laravel Queues
      • Laravel Octane
    • Ecotone Lite
      • Logging
      • Database Connection (DBAL Module)
    • JMS Converter
    • OpenTelemetry (Tracing and Metrics)
      • Configuration
    • RabbitMQ Support
    • Kafka Support
      • Configuration
      • Message partitioning
      • Usage
    • DBAL Support
    • Amazon SQS Support
    • Redis Support
  • Other
    • Contact, Workshops and Support
Powered by GitBook
On this page
  • Ecotone Lite
  • Configuring Ecotone Lite for your tests
  • Calling Command Bus
  • Calling Event Bus
  • Verifying Published Events
  • Verifying Message Headers
  • Verifying Commands/Queries
  • Discarding recorded messages
  • Caching Configuration

Was this helpful?

Export as PDF
  1. Modelling
  2. Testing Support

Testing Messaging

Testing Messaging architecture in PHP

Testing Message Driven Architecture can easily become nightmare from the perspective of speed and maintenance of such tests. It's really important to write easy to understand, quick and reliable test scenarios. especially in long term projects. That's why Ecotone comes with test supporting tools, which helps writing tests that are close to the way code runs in production, yet kept simple and isolated.

Ecotone Lite

Ecotone Lite is a way to run Ecotone Application with full possibility to customize it. You may point exactly what classes you would like to run, turn on and off modules that you want to include or exclude.

This makes Ecotone Lite great solution for running your tests in a way that they are really close to the way your code works on production with isolation to the set of classes you would like to test.

Configuring Ecotone Lite for your tests

$ecotoneLite = EcotoneLite::bootstrapFlowTesting(
    // 1. Classes to resolve
    [User::class],
    // 2. Available services, you may inject container instead
    [new EmailConverter(), new PhoneNumberConverter(), new UuidConverter()],
    // 3. Service Configuration
    ServiceConfiguration::createWithDefaults()
        // 4. resolve all classes from given namespace
        ->withNamespaces(["App\Testing\Infrastructure\Converter"])
        // 5 add extension objects
        ->withExtensionObjects([
            // 6. register in memory repository for User
            InMemoryStateStoredRepositoryBuilder::createForAllAggregates()
        ])
        // 7. Turn off given Ecotone's modules
        ->withSkippedModulePackageNames([
            // 8. Turning off asynchronous package
            ModulePackageList::ASYNCHRONOUS_PACKAGE
        ])
);
  1. Classes to resolve - The first parameter, sets list of classes that you would like to include in this test. Those are classes that containing Ecotone's attributes.

  2. Available services - Provides references to the service classes used in this test. You may inject PSR compatible container instead.

  3. Service Configuration -withNamespaces - One of the configuration worth mention is withNamespaces this allows to use define given or set of namespaces, from which all classes will be included in this test.

  4. Service Configuration -withExtensionObjects - This allows for defining extension object for customizing Ecotone's modules

  5. Turn off Modules - This allows you to turn off given Ecotone's module for this test.

  6. Turning off asynchronous package - Turning off asynchronous package for example, will make your code execute synchronously, so you won't need to bother with running consumers in your test.

Calling Command Bus

Suppose we have user registration Command Handler:

class UserService
{
    #[CommandHandler]
    public function register(RegisterUser $command, UserRepository $userRepository)
    {
        $userRepository->save(User::create($command->name));
    }
}

and we want to fetch, if User was stored in the repository after sending an Command.

$userRepository = new InMemoryUserRepository;

$ecotoneLite = EcotoneLite::bootstrapFlowTesting(
    // We provide list of classes which are using Ecotone's attributes
    [UserService::class],
    // We provide Service used by the Command Handler
    [UserRepository::class => $userRepository]
);

// No users in repository before calling command
$this->assertEmpty($userRepository->getAll());

$ecotoneLite->sendCommand(new RegisterUser(
    Uuid::uuid4(),
    "johny",
    Email::create("test@wp.pl"),
    PhoneNumber::create("148518518518"))
));

// User should be stored in repository
$this->assertNotEmpty($userRepository->getAll());

The same way we send Command using sendCommand, we may send Queries - sendQuery and publish Events - publishEvent.

Calling Event Bus

We may use EventBus or publish event directly using publishEvent support:

$ecotoneLite->publishEvent(new OrderWasPlaced(Uuid::uuid4()->toString());

This is useful when we are testing Event Handlers directly.

#[EventHandler]
public function whenOrderWasCancelled(OrderWasPlaced $event): void
{
    // something happens
}

Verifying Published Events

After sending Command, you may verify, if given set of events were published as a result.

For this Ecotone introduce Message Collector which intercept your message flow.

$this->assertEquals(
    [new OrderWasPlaced($orderId)], 
    $ecotoneLite->getRecordedEvents()
);

Ecotone intercept all interactions with Event/Command/Query Buses. This allows you to spy on all recorded messages, to verify their state.

Verifying Message Headers

If you want to validate, if Event was sent with given set of headers:

$this->assertEquals(
    $executorId, 
    $ecotoneLite->getRecordedEventMessages()[0]->getHeaders()->get('executorId')
);

Verifying Commands/Queries

You may also verify, if Command or Query was sent:

    /**
     * @return array<int, mixed>
     */
    public function getRecordedCommands(): array;

    /**
     *  Allows to assert metadata of the message
     *
     * @return Message[]
     */
    public function getRecordedCommandMessages(): array;

    /**
     * @return array<int, mixed>
     */
    public function getRecordedQueries(): array;

    /**
     *  Allows to assert metadata of the message
     *
     * @return Message[]
     */
    public function getRecordedQueryMessages(): array;
    
    /**
     * @return array<int, mixed>
     */
    public function getRecordedEvents(): array

Discarding recorded messages

In case you're not interested in current messages, you may clean up Message Collector

$ecotoneLite->discardRecordedMessages();

Caching Configuration

Ecotone Lite tests are quick to run as the boot minimal version of Ecotone which is supposed to handle given set of classes. This way we avoid booting whole Application in order to run tests that cover specific scenario.

The only part which add extra milliseconds to the tests execution, is bootstrapping the configuration. However Ecotone does cache it between the test runs and mark it as stale when related test files does change. This means we may have great volume of tests using Ecotone Lite, and execution speed will be preserved.

By default if not changed Ecotone Lite, will store the config in temporary folder under ecotone catalog: "/tmp/ecotone". Ecotone will reload cache on configuration changes, yet we may always remove the catalog manually in case.

PreviousTesting SupportNextTesting Aggregates and Sagas with Message Flows

Last updated 1 year ago

Was this helpful?

Service Configuration - In here you may customize your configuration for this test case. All configs can be .

Register in memory repository - This example extension object registers In Memory Repository for all your

If you want to read more about Ecotone Lite configuration, check .

state stored aggregates
Module Page
find here