diff --git a/pom.xml b/pom.xml
index 9e63b2f..cdf7a82 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
17
3.1.0
1.3.2
- 1.0-alpha-13
+ 0.2.4
2.41.1
@@ -125,9 +125,9 @@
test
- org.wiremock.integrations.testcontainers
- wiremock-testcontainers-module
- ${wiremock-testcontainers-module.version}
+ io.github.microcks
+ microcks-testcontainers
+ ${microcks-testcontainers-module.version}
test
diff --git a/src/main/resources/catalog-openapi.yaml b/src/main/resources/catalog-openapi.yaml
new file mode 100644
index 0000000..30809bc
--- /dev/null
+++ b/src/main/resources/catalog-openapi.yaml
@@ -0,0 +1,96 @@
+openapi: 3.0.2
+info:
+ title: Catalog Service
+ version: 1.0
+ description: API definition of Catalog Service
+ license:
+ name: MIT License
+ url: https://opensource.org/licenses/MIT
+paths:
+ /api/products/{code}:
+ get:
+ parameters:
+ - name: code
+ description: product code
+ schema:
+ type: string
+ in: path
+ required: true
+ examples:
+ P101:
+ value: P101
+ P102:
+ value: P102
+ P103:
+ value: P103
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Product'
+ examples:
+ P101:
+ value:
+ id: 1
+ code: P101
+ name: Product P101
+ description: Product P101 description
+ imageUrl: null
+ price: 34.0
+ available: true
+ P102:
+ value:
+ id: 1
+ code: P101
+ name: Product P102
+ description: Product P102 description
+ imageUrl: null
+ price: 25.0
+ available: true
+ P103:
+ value:
+ id: 3
+ code: P103
+ name: Product P103
+ description: Product P103 description
+ imageUrl: null
+ price: 15.0
+ available: false
+components:
+ schemas:
+ Product:
+ title: Root Type for catalog Product
+ type: object
+ properties:
+ id:
+ description: Unique identifier of this product
+ type: number
+ code:
+ description: Code of this product
+ type: string
+ name:
+ description: Name of this product
+ type: string
+ description:
+ description: Description of this product
+ type: string
+ imageUrl:
+ description: Url of image of this product
+ type: string
+ nullable: true
+ price:
+ description: Price of this product
+ type: number
+ available:
+ description: Availability of this product
+ type: boolean
+ required:
+ - id
+ - code
+ - name
+ - description
+ - price
+ - imageUrl
+ - available
+ additionalProperties: false
\ No newline at end of file
diff --git a/step-1-getting-started.md b/step-1-getting-started.md
index 9c2b99c..9120542 100644
--- a/step-1-getting-started.md
+++ b/step-1-getting-started.md
@@ -74,7 +74,7 @@ This might be helpful if the internet connection at the workshop venue is somewh
```shell
docker pull postgres:16-alpine
docker pull localstack/localstack:2.3
-docker pull wiremock/wiremock:3.2.0-alpine
+docker pull quay.io/microcks/microcks-uber:1.8.1
docker pull confluentinc/cp-kafka:7.5.0
docker pull confluentinc/cp-schema-registry:7.5.0
docker pull confluentinc/cp-enterprise-control-center:7.5.0
diff --git a/step-2-exploring-the-app.md b/step-2-exploring-the-app.md
index f32946e..2270aca 100644
--- a/step-2-exploring-the-app.md
+++ b/step-2-exploring-the-app.md
@@ -30,7 +30,7 @@ and `com.testcontainers.catalog.events.ProductEventListener`.
## External Service Integrations
Our application talks to `inventory-service` to fetch the product availability information.
-We will use [WireMock](https://wiremock.org/) to mock the `inventory-service` during local development and testing.
+We will use [Microcks](https://microcks.io/) to mock the `inventory-service` during local development and testing.
## API Endpoints
diff --git a/step-3-local-development-environment.md b/step-3-local-development-environment.md
index 2e7ad9a..6f3cb86 100644
--- a/step-3-local-development-environment.md
+++ b/step-3-local-development-environment.md
@@ -238,79 +238,96 @@ When we invoke the `GET /api/products/{code}` API endpoint,
the application tried to call the inventory service to get the inventory details.
As the inventory service is not running, we get the above error.
-Let's use WireMock to mock the inventory service APIs for our local development and testing.
+Let's use Microcks to mock the inventory service APIs for our local development and testing.
-## Configure WireMock
+## Configure Microcks
Add the following dependency to your `pom.xml`:
```xml
- org.wiremock.integrations.testcontainers
- wiremock-testcontainers-module
- 1.0-alpha-13
+ io.github.microcks
+ microcks-testcontainers
+ 0.2.4
test
```
-Create `src/test/resources/mocks-config.json` to define Mock API behaviour.
-
-```json
-{
- "mappings": [
- {
- "request": {
- "method": "GET",
- "urlPattern": "/api/inventory/P101"
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json"
- },
- "jsonBody": {
- "code": "P101",
- "quantity": 25
- }
- }
- },
- {
- "request": {
- "method": "GET",
- "urlPattern": "/api/inventory/P102"
- },
- "response": {
- "status": 500,
- "headers": {
- "Content-Type": "application/json"
- }
- }
- },
- {
- "request": {
- "method": "GET",
- "urlPattern": "/api/inventory/P103"
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json"
- },
- "jsonBody": {
- "code": "P103",
- "quantity": 0
- }
- }
- }
- ]
-}
+Imagine you got an OpenAPI definition for the inventory service from the service provider.
+Create `src/test/resources/inventory-openapi.yaml` with this content; this will define the Mocks behaviour:
+
+```yaml
+openapi: 3.0.2
+info:
+ title: Inventory Service
+ version: 1.0
+ description: API definition of Inventory Service
+ license:
+ name: MIT License
+ url: https://opensource.org/licenses/MIT
+paths:
+ /api/inventory/{code}:
+ get:
+ parameters:
+ - name: code
+ description: product code
+ schema:
+ type: string
+ in: path
+ required: true
+ examples:
+ P101:
+ value: P101
+ P102:
+ value: P102
+ P103:
+ value: P103
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Product'
+ examples:
+ P101:
+ value:
+ code: P101
+ quantity: 25
+ P103:
+ value:
+ code: P103
+ quantity: 0
+ "500":
+ content:
+ application/json:
+ schema:
+ type: string
+ examples:
+ P102:
+ value: ""
+components:
+ schemas:
+ Product:
+ title: Root Type for Product
+ type: object
+ properties:
+ code:
+ description: Code of this product
+ type: string
+ quantity:
+ description: Remaining quantity for this product
+ type: number
+ required:
+ - code
+ - quantity
+ additionalProperties: false
```
-Next, update the `ContainersConfig` class to configure the `WireMockContainer` as follows:
+Next, update the `ContainersConfig` class to configure the `MicrocksContainer` as follows:
```java
package com.testcontainers.catalog;
-import org.wiremock.integrations.testcontainers.WireMockContainer;
+import io.github.microcks.testcontainers.MicrocksContainer;
@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfig {
@@ -318,17 +335,21 @@ public class ContainersConfig {
// [...]
@Bean
- WireMockContainer wiremockServer(DynamicPropertyRegistry registry) {
- WireMockContainer wiremockServer = new WireMockContainer("wiremock/wiremock:3.2.0-alpine")
- .withMappingFromResource("mocks-config.json");
- registry.add("application.inventory-service-url", wiremockServer::getBaseUrl);
- return wiremockServer;
+ MicrocksContainer microcksContainer(DynamicPropertyRegistry registry) {
+ MicrocksContainer microcks = new MicrocksContainer("quay.io/microcks/microcks-uber:1.8.1")
+ .withMainArtifacts("inventory-openapi.yaml")
+ .withAccessToHost(true);
+
+ registry.add(
+ "application.inventory-service-url", () -> microcks.getRestMockEndpoint("Inventory Service", "1.0"));
+
+ return microcks;
}
}
```
-Once the WireMock server is started, we are registering the WireMock server URL as `application.inventory-service-url`.
-So, when we make a call to `inventory-service` from our application, it will call the WireMock server instead.
+Once the Microcks server is started, we are registering the Microcks provided mock endpoint as `application.inventory-service-url`.
+So, when we make a call to `inventory-service` from our application, it will call the Microcks endpoint instead.
Now restart the `TestApplication` and invoke the `GET /api/products/P101` API again.
@@ -367,7 +388,7 @@ You should get the following response with `"available":false` because we mocked
}
```
-Now we have a working local development environment with PostgreSQL, Kafka, LocalStack, and WireMock.
+Now we have a working local development environment with PostgreSQL, Kafka, LocalStack, and Microcks.
###
[Next](step-4-connect-to-services.md)
diff --git a/step-5-write-tests.md b/step-5-write-tests.md
index 826b9af..cac22be 100644
--- a/step-5-write-tests.md
+++ b/step-5-write-tests.md
@@ -5,7 +5,7 @@ But there is nothing more painful than working on a codebase without a comprehen
Let's fix that!!
## Common Test SetUp
-For all the integration tests in our application, we need to start PostgreSQL, Kafka, LocalStack and WireMock containers.
+For all the integration tests in our application, we need to start PostgreSQL, Kafka, LocalStack and Microcks containers.
So, let's create a `BaseIntegrationTest` class under `src/test/java` with the common setup as follows:
```java
@@ -19,7 +19,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.context.annotation.Import;
-import org.springframework.test.context.ActiveProfiles;
+import org.testcontainers.Testcontainers;
@SpringBootTest(
webEnvironment = RANDOM_PORT,
@@ -35,6 +35,7 @@ public abstract class BaseIntegrationTest {
@BeforeEach
void setUpBase() {
RestAssured.port = port;
+ Testcontainers.exposeHostPorts(port);
}
}
```
@@ -209,9 +210,74 @@ class ProductControllerTest extends BaseIntegrationTest {
}
```
+Checking product information like this is easy but become really cumbersome when the number of properties is growing
+or when the `Product` class is shared among many different operations of your API. You have to check the properties
+presence but also their type and this can result in sprawling code!
+
+If you're using an "API design-first approach", the conformance of your data structure can be automatically checked by
+Microcks for you! Check the `src/main/resources/catalog-openapi.yaml` file that describes our Catalog API.
+
+Now let's create a test that uses Microcks to automatically check that our `ProductController` is conformance to this definition:
+
+```java
+import io.github.microcks.testcontainers.MicrocksContainer;
+import io.github.microcks.testcontainers.model.TestRequest;
+import io.github.microcks.testcontainers.model.TestResult;
+import io.github.microcks.testcontainers.model.TestRunnerType;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.test.context.jdbc.Sql;
+
+@Sql("/test-data.sql")
+class ProductControllerTest extends BaseIntegrationTest {
+ @Autowired
+ MicrocksContainer microcks;
+
+ @Test
+ void checkOpenAPIConformance() throws Exception {
+ microcks.importAsMainArtifact(new ClassPathResource("catalog-openapi.yaml").getFile());
+
+ TestRequest testRequest = new TestRequest.Builder()
+ .serviceId("Catalog Service:1.0")
+ .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
+ .testEndpoint("http://host.testcontainers.internal:" + RestAssured.port)
+ .build();
+
+ TestResult testResult = microcks.testEndpoint(testRequest);
+
+ assertThat(testResult.isSuccess()).isTrue();
+ }
+}
+```
+
+Let's understand what's going on behind the scenes:
+* We complete the Microcks container with our additional `catalog-openapi.yaml` artifact file (this could have also
+been done within the `ContainersConfig` class at bean initialisation).
+* We prepare a `TestRequest` object that allows to specify the scope of the conformance test. Here we want to check the
+conformance of `Catalog Service` with version `1.0` that are the identifier found in `catalog-openapi.yaml`.
+* We ask Microcks to validate the `OpenAPI Schema` conformance by specifying a `runnerType`.
+* We ask Microcks to validate the localhost endpoint on the dynamic port provided by the Spring Test
+(we use the `host.testcontainers.internal` alias for that).
+
+Finally, we're retrieving a `TestResult` from Microcks containers, and we can assert stuffs on this result, checking it's a success.
+
+During the test, Microcks has reused all the examples found in the `catalog-openapi.yaml` file to issue requests to
+our running application. It also checked that all the received responses conform to the OpenAPI definition elements:
+return codes, headers, content-type and JSON schema structure.
+
+If you want to get more details on the test done by Microcks, you can add those lines just before the `assertThat()`:
+
+```java
+ // You may inspect complete response object with following:
+ ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(testResult));
+```
+
## Assignment
* Write tests for create product API fails if the payload is invalid.
* Write tests for create product API fails if the product code already exists.
* Write tests for get product by code API fails if the product code does not exist.
-* Write tests for get product by code API that returns `"available": false` when WireMock server return quantity=0.
-* Write tests for get product by code API that returns `"available": true` from WireMock server throws Exception.
+* Write tests for get product by code API that returns `"available": false` when Microcks server return quantity=0.
+* Write tests for get product by code API that returns `"available": true` from Microcks server throws Exception.