Test the asynchronous API

Edit on GitHub

This document describes how to set up and run AsyncAPI tests. We use the Hello World example throughout this document. All code references the Hello World App and the Pyz project namespace. When you set up and run the tests for a different project namespace or module, adjust the names accordingly.

Prerequisites

Make sure the following prerequisites are met:

  1. Spryker Testify version 3.50.0 or later is installed. The AsyncAPI SDK is required by this package, however, you don’t need to install it manually.
  • To verify the installation status and version of Spryker Testify, run the following command:
composer info spryker/testify
  • To install Spryker Testify, run the following command:
composer require --dev "spryker/testify:^3.50.0"
  • To update Spryker Testify, run the following command:
composer update "spryker/testify:^3.50.0"
  1. Spryker Testify AsyncAPI version 0.1.1 or later is installed. The AsyncAPI SDK is required by this package, however, you don’t need to install it manually.
  • To verify the installation status and version of Spryker Testify AsyncAPI, run the following command:
composer info spryker/testify-async-api
  • To install Spryker Testify AsyncAPI, run the following command:
composer require --dev "spryker/testify-async-api:^0.1.1"
  • To update Spryker Testify AsyncAPI run the following command:
composer update "spryker/testify-async-api:^0.1.1"
  1. Spryks version 0.5.2 or later is installed.
  • To verify the installation status and version of Spryks, run the following command:
composer info spryker-sdk/spryk
  • To install Spryks, run the following command:
composer require --dev "spryker-sdk/spryk:^0.5.2"
  • To update Spryks, run the following command:
composer update "spryker-sdk/spryk:^0.5.2"
  1. There is a valid AsyncAPI schema file in resources/api/asyncapi.yml. To create the file, you can use the example provided in Hello World App AsyncAPI.

Testing the asynchronous API

Testing the asynchronous API implies that all schema files are tested to ensure that they align with the code that handles or produces messages. Each module that has an AsyncAPI schema file must have a dedicated test suite.

To test the asynchronous API, follow these steps:

1. Generate the code

To generate the code, you need to provide a valid schema file within your app. The schema file must reside in resources/api/asyncapi.yml. In the following example, we use the file provided in the Hello World App AsyncAPI, which you can also use as a starting point for your project. After you have added the schema file, run the code generator for it using the following command:

docker/sdk cli vendor/bin/asyncapi code:asyncapi:generate -o Pyz

This command adds relevant modules and tests to your project to get you started with the asynchronous API in the src/ and tests/ directories.

To verify the introduced changes, check the src/ and tests/ directories.

2. Build Codeception

Run the following Codeception build command:

docker/sdk cli vendor/bin/codecept build -c tests/PyzTest/AsyncApi/HelloWorld

Not everything can be automatically generated with these commands. You also need to update the Codeception configuration and the project configuration as described in the following sections.

3. Update the Codeception configuration

Open the created Codeception configuration file at tests/PyzTest/AsyncApi/HelloWorld/codeception.yml and add the generated handlers to the configuration of AsyncApiHelper.

The file should contain the following section:

\Spryker\Zed\TestifyAsyncApi\Business\Codeception\Helper\AsyncApiHelper:
    asyncapi: resources/api/asyncapi.yml
    handlers:
        - \Pyz\Zed\HelloWorld\Communication\Plugin\MessageBroker\UserCreatedMessageHandlerPlugin

Depending on your schema file, you need to add your specific handlers. All handlers are located in the src/Pyz/Zed/HelloWorld/Communication/Plugin/MessageBroker directory. Add the class name of each handler to your Codeception configuration.

4. Update the project configuration

When testing the asynchronous API, all messages must be sent to the local message broker transport. This should only happen when you test the API with automated tests.

Add the following configuration to the config/Shared/config_local.php file:

use Spryker\Shared\MessageBroker\MessageBrokerConstants;

$config[MessageBrokerConstants::IS_ENABLED] = true;
$config[MessageBrokerConstants::MESSAGE_TO_CHANNEL_MAP] = [
    '*' => 'test-channel',
];

$config[MessageBrokerConstants::CHANNEL_TO_TRANSPORT_MAP] = [
    'test-channel' => 'local',
];
Warning

This is a very generic configuration and shouldn’t be used in a production environment.

5. Run the tests

Run the tests using the following command:

docker/sdk testing vendor/bin/codecept run -c tests/PyzTest/AsyncApi/HelloWorld

Once the testing process is complete, you get the result of each individual test.

Example test methods

This section lists some example methods and explains what and how they test.

Handling messages

Here is the example of handling the messages:

public function testUserCreatedMessageCreatesAUserEntity(): void
{
    // Arrange
    $userCreatedTransfer = $this->tester->haveUserCreatedTransfer();

    // Act
    $this->tester->runMessageReceiveTest($userCreatedTransfer, 'user-events');

    // Assert
    $this->tester->assert...(...);
}

In the Arrange section, implement the code to allow for a message transfer that you expect to receive from another application.

In the Act section, call runMessageReceiveTest in AsyncApiHelper. The runMessageReceiveTest method takes the message that you expect to receive as its first argument and the channel name where you expect the message to come through as its second argument. Internally, the message and the channel name are validated against your asyncapi.yml schema file to ensure that both meet the definition.

In the Assert section, you make assertions based on your business logic. For example, you might verify the existence of a database entry after the message has been processed.

The underlying AsyncApiHelper ensures the following inside the runMessageReceiveTest method:

  • The message handler can handle the message.
  • The expected channel name exists in the schema file.
  • The expected message name exists in the schema file.
  • The message contains all required attributes defined in the schema file.

The AsyncApiHelper also executes the handler with the passed message. After the runMessageReceiveTest method execution, you need to make your assertions, such as verifying that a specific change was made in your database after processing the message. The only remaining tasks for you are to implement the business logic, update the tests according to your business logic, and then run the tests. To run the tests, use the following command:

vendor/bin/codecept run -c tests/PyzTest/AsyncApi/HelloWorld/

Publishing messages

Here is the example of publishing the messages:

public function testGreetUserMessageIsEmittedWhenUserWasStoredInTheDatabase(): void
{
    // Arrange
    $expectedGreetUserTransfer = $this->tester->haveGreetUserTransfer();

    // Act
    $this->tester->getFacade()->saveUser(...);

    // Assert
    $this->tester->assertMessageWasEmittedOnChannel($expectedGreetUserTransfer, 'user-commands');
}

In the Arrange section, prepare a message transfer that you expect to be sent once your business logic is executed.

In the Act section, call your business logic. For example, you could call a facade method where you expect the message to be sent.

In the Assert section, call the assertMessageWasEmittedOnChannel method with the expected message you created in the Arrange section. The first argument is the message that you expect to be sent, and the second argument is the channel name through which you expect the message to be sent. Internally, the message and the channel name are validated against your asyncapi.yml schema file to ensure that both meet the definition.

The underlying AsyncApiHelper ensures the following inside the assertMessageWasEmittedOnChannel method:

  • The expected channel name exists in the schema file.
  • The expected message name exists in the schema file.
  • The message was sent with all required attributes defined in the schema file.