Skip to content

Commit

Permalink
Enhancing documentation with schemas and sequences
Browse files Browse the repository at this point in the history
Signed-off-by: Laurent Broudoux <laurent.broudoux@gmail.com>
  • Loading branch information
lbroudoux committed Feb 1, 2024
1 parent ed454da commit 6eea4b1
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 3 deletions.
Binary file added assets/test-order-event-consumer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/test-order-event-publisher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/test-order-service-api.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/test-pastry-api-client.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 44 additions & 2 deletions step-4-write-rest-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public abstract class BaseIntegrationTest {

## First Test - Verify our RESTClient

In this section, we'll focus on testing the `Pastry API Client` component of our application:

![Pastry API Client](./assets/test-pastry-api-client.png)

Let's review the test class `PastryAPIClientTests` under `src/test/java/org/acme/order/client`:

```java
Expand Down Expand Up @@ -91,9 +95,27 @@ and that they're correctly wired to the application. Within this test:
* The `PastryAPIClient` has been configured with a REST Client that is wired to the Microcks mock endpoints.
* We're validating the configuration of this client as well as all the JSON and network serialization details of our configuration!

The sequence diagram below details the test sequence. Microcks is used as a third-party backend to allow going through all the layers:

```mermaid
sequenceDiagram
PastryAPIClientTests->>+PastryAPIClient: listPastries("S")
PastryAPIClient->>+RESTClient: get()
RESTClient->>+Microcks: GET /pastries?size=S
participant Microcks
Note right of Microcks: Initialized at test startup
Microcks-->>-RESTClient: HTTP Response w. JSON[]
RESTClient-->>-PastryAPIClient: Response
PastryAPIClient-->>-PastryAPIClientTests: List<Pastry>
```

## Second Test - Verify the technical conformance of Order Service API

The 2nd thing we want to validate is the conformance of the `Order API` we'll expose to consumers.
The 2nd thing we want to validate is the conformance of the `Order API` we'll expose to consumers. In this section and the next one,
we'll focus on testing the `OrderController` component of our application:

![Order Controller Test](./assets/test-order-service-api.png)

We certainly can write an integration test that uses [Rest Assured](https://rest-assured.io/) or other libraries
to invoke the exposed HTTP layer and validate each and every response with Java assertions like:

Expand Down Expand Up @@ -147,6 +169,23 @@ test we want to run:

Finally, we're retrieving a `TestResult` from Microcks containers, and we can assert stuffs on this result, checking it's a success.

The sequence diagram below details the test sequence. Microcks is used as a middleman that actually invokes your API with the example from its dataset:

```mermaid
sequenceDiagram
OrderControllerContractTests->>+Microcks: testEndpoint()
participant Microcks
Note right of Microcks: Initialized at test startup
loop For each example in Microcks
Microcks->>+OrderController: HTTP Request
OrderController->>+OrderService: business logic
OrderService-->-OrderController: response
OrderController-->-Microcks: HTTP Response
Microcks-->Microcks: validate Response
end
Microcks-->-OrderControllerContractTests: TestResult
```

Our `OrderController` development is technically correct: all the JSON and HTTP serialization layers have been tested!

## Third Test - Verify the business conformance of Order Service API
Expand Down Expand Up @@ -196,10 +235,13 @@ public class OrderControllerPostmanContractTests extends BaseIntegrationTest {
}
```

Comparing to the code in previous section, the lony change here is that we asked Microcks to use a `Postman` runner
Comparing to the code in previous section, the only change here is that we asked Microcks to use a `Postman` runner
for executing our conformance test. What happens under the hood is now that Microcks is re-using the collection snippets
to put some constraints on API response and check their conformance.

The test sequence is exactly the same as in the previous section. The difference here lies in the type of response validation: Microcks
reuses Postman collection constraints.

You're now sure that beyond the technical conformance, the `Order Service` also behaves as expected regarding business
constraints.

Expand Down
70 changes: 69 additions & 1 deletion step-5-write-async-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Testing of asynchronous or event-driven system is usually a pain for developers

## First Test - Verify our OrderService is publishing events

In this section, we'll focus on testing the `Event Publisher` component of our application:

![Event Publisher Test](./assets/test-order-event-publisher.png)

Even if it may be easy to check that the creation of an event object has been triggered with frameworks like [Mockito](https://site.mockito.org/)
or others, it's far more complicated to check that this event is correctly serialized, sent to a broker and valid
regarding an Event definition...
Expand Down Expand Up @@ -80,11 +84,40 @@ Things are a bit more complex here, but we'll walk through step-by-step:
* We can invoke our business service by creating an order with `placeOrder()` method. We could assert whatever we want on created order as well.
* Finally, we wait for the future completion to retrieve the `TestResult` and assert on the success and check we received 1 message as a result.

The sequence diagram below details the test sequence. You'll see 2 parallel blocks being executed:
* One that corresponds to Microcks test - where it connects and listen for Kafka messages,
* One that corresponds to the `OrderService` invokation that is expected to trigger a message on Kafka.

```mermaid
sequenceDiagram
par Launch Microcks test
OrderServiceTests->>Microcks: testEndpointAsync()
participant Microcks
Note right of Microcks: Initialized at test startup
Microcks->>Kafka: poll()
Kafka-->>Microcks: messages
Microcks-->Microcks: validate messages
and Invoke OrderService
OrderServiceTests->>+OrderService: placeOrder(OrderInfo)
OrderService->>+OrderEventPublisher: publishEvent(OrderEvent)
OrderEventPublisher->>Kafka: send("orders-created")
OrderEventPublisher-->-OrderService: done
OrderService-->-OrderServiceTests: Order
end
OrderServiceTests->>+Microcks: get()
Note over OrderServiceTests,Microcks: After at most 2 seconds
Microcks-->OrderServiceTests: TestResult
```

Because the test is a success, it means that Microcks has received an `OrderEvent` on the specified topic and has validated the message
conformance with the AsyncAPI contract or this event-driven architecture. So you're sure that all your Spring Boot configuration, Kafka JSON serializer
configuration and network communication are actually correct!

## Second Test - Verify our OrderEventList is processing events
## Second Test - Verify our OrderEventListener is processing events

In this section, we'll focus on testing the `Event Consumer` component of our application:

![Event Publisher Test](./assets/test-order-event-consumer.png)

The final thing we want to test here is that our `OrderEventListener` component is actually correctly configured for connecting to Kafka,
for consuming messages, for de-serializing them into correct Java objects and for triggering the processing on the `OrderService`.
Expand Down Expand Up @@ -134,6 +167,41 @@ The important things to get in this test are:
* If we retrieve this order and get the correct information from the service, it means that is has been received and correctly processed!
* If no message is found before the end of 4 seconds, the loop exits with a `ConditionTimeoutException` and we mark our test as failed.

The sequence diagram below details the test sequence. You'll see 3 parallel blocks being executed:
* The first corresponds to Microcks mocks - where it connects to Kafka, creates a topic and publishes sample messages each 3 seconds,
* The second one corresponds to the `ORderEventListener` invocation that should be triggered when a message is found in the topic,
* The third one corresponds to the actual test - where we check that the specified order has been found and processed by the `OrderService`.

```mermaid
sequenceDiagram
par On test startup
loop Each 3 seconds
participant Microcks
Note right of Microcks: Initialized at test startup
Microcks->>Kafka: send(microcks-orders-reviewed")
end
and Listener execution
OrderEventListener->>Kafka: poll()
Kafka-->>OrderEventListener: messages
OrderEventListener->>+OrderService: updateOrder()
OrderService-->OrderService: update order status
OrderService->>-OrderEventListener: done
and Test execution
Note over OrderServiceTests,OrderService: At most 4 seconds
loop Each 400ms
OrderEventListenerTests->>+OrderService: getOrder("123-456-789")
OrderService-->-OrderEventListenerTests: order or throw OrderNotFoundException
alt Order "123-456-789" found
OrderEventListenerTests-->OrderEventListenerTests: assert and break;
else Order "123-456-789" not found
OrderEventListenerTests-->OrderEventListenerTests: continue;
end
end
end
```

You did it and succeed in writing integration tests for all your application component with minimum boilerplate code! 🤩

## Wrap-up

Thanks a lot for being through this quite long demonstration. We hope you learned new techniques for integration tests with both REST and Async/Event-driven APIs. Cheers! 🍻

0 comments on commit 6eea4b1

Please sign in to comment.