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
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.
$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 injectPSR compatible container
instead. - 3.Service Configuration - In here you may customize your configuration for this test case. All configs can be find here.
- 4.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. - 5.Service Configuration -withExtensionObjects - This allows for defining extension object for customizing Ecotone's modules
- 6.Register in memory repository - This example extension object registers In Memory Repository for all your state stored aggregates
- 7.Turn off Modules - This allows you to turn off given Ecotone's module for this test.
- 8.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.
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("[email protected]"),
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.
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.
If you want to validate, if Event was sent with given set of headers:
$this->assertEquals(
$executorId,
$ecotoneLite->getRecordedEventMessages()[0]->getHeaders()->get('executorId')
);
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
In case you're not interested in current messages, you may clean up
Message Collector
$ecotoneLite->discardRecordedMessages();
Last modified 1mo ago