### Verify Docker Installation Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step1-getting-started.md Check your Docker installation by running the 'docker version' command. This confirms that Docker is installed and accessible. ```bash $ docker version Client: Version: 28.1.1 API version: 1.49 Go version: go1.23.8 Git commit: 4eba377 Built: Fri Apr 18 09:49:45 2025 OS/Arch: darwin/arm64 Context: desktop-linux Server: Docker Desktop 4.41.2 (191736) Engine: Version: 28.1.1 API version: 1.49 (minimum version 1.24) Go version: go1.23.8 Git commit: 01f442b Built: Fri Apr 18 09:52:08 2025 OS/Arch: linux/arm64 Experimental: false containerd: Version: 1.7.27 GitCommit: 05044ec0a9a75232cad458027ca83437aae3f4da runc: Version: 1.2.5 GitCommit: v1.2.5-0-g59923ef docker-init: Version: 0.19.0 GitCommit: de40ad0 ``` -------------------------------- ### IClassFixture Pattern Example Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/explain-integration-testing-patterns.md Demonstrates the use of IClassFixture for creating isolated test classes, where each class gets its own factory and container instances. ```csharp public class MyTestClass : IClassFixture> { // Each test class gets its own factory instance // Each factory starts its own containers } ``` -------------------------------- ### Run Microcks with Docker Compose Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step3-local-development.md Starts Microcks and its dependencies using a docker-compose file. Ensure Microcks is configured to import API contracts. ```shell ./microcks.sh [+] Running 4/4 ✔ Container microcks-kafka Started 0.1s ✔ Container microcks-testcontainers-dotnet-demo-microcks-1 Started 0.1s ✔ Container microcks-async-minion Started 0.1s ✔ Container microcks-testcontainers-dotnet-demo-importer-1 Started 0.1s ``` -------------------------------- ### OrderServiceWebApplicationFactory for IClassFixture Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/explain-integration-testing-patterns.md Implements a WebApplicationFactory for IClassFixture, ensuring dynamic port allocation for Kestrel, Kafka, and other services to avoid conflicts. This factory starts its own Kafka and Microcks containers for each test class. ```csharp public class OrderServiceWebApplicationFactory : WebApplicationFactory, IAsyncLifetime where TProgram : class { public ushort ActualPort { get; private set; } public KafkaContainer KafkaContainer { get; private set; } = null!; public MicrocksContainerEnsemble MicrocksContainerEnsemble { get; private set; } = null!; private ushort GetAvailablePort() { using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, 0)); return (ushort)((IPEndPoint)socket.LocalEndPoint!).Port; } public async ValueTask InitializeAsync() { // CRITICAL: Get dynamic port for each instance ActualPort = GetAvailablePort(); UseKestrel(ActualPort); await TestcontainersSettings.ExposeHostPortsAsync(ActualPort, TestContext.Current.CancellationToken); var network = new NetworkBuilder().Build(); // Each instance gets its own Kafka container KafkaContainer = new KafkaBuilder() .WithImage("confluentinc/cp-kafka:7.9.0") .WithPortBinding(0, KafkaBuilder.KafkaPort) // 0 = dynamic port .WithNetwork(network) .WithNetworkAliases("kafka") .Build(); await KafkaContainer.StartAsync(TestContext.Current.CancellationToken); // Each instance gets its own Microcks container MicrocksContainerEnsemble = new MicrocksContainerEnsemble(network, "quay.io/microcks/microcks-uber:1.13.0") .WithAsyncFeature() .WithMainArtifacts("resources/order-service-openapi.yaml") .WithKafkaConnection(new KafkaConnection($"kafka:19092")); await MicrocksContainerEnsemble.StartAsync(); } protected override void ConfigureWebHost(IWebHostBuilder builder) { base.ConfigureWebHost(builder); var pastryApiEndpoint = MicrocksContainerEnsemble.MicrocksContainer .GetRestMockEndpoint("API Pastries", "0.0.1"); builder.UseSetting("PastryApi:BaseUrl", pastryApiEndpoint); var kafkaBootstrap = KafkaContainer.GetBootstrapAddress() .Replace("PLAINTEXT://", "", StringComparison.OrdinalIgnoreCase); builder.UseSetting("Kafka:BootstrapServers", kafkaBootstrap); } public async override ValueTask DisposeAsync() { await base.DisposeAsync(); await KafkaContainer.DisposeAsync(); await MicrocksContainerEnsemble.DisposeAsync(); } } ``` -------------------------------- ### Isolated Test Class Implementation (Pattern 1) Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/explain-integration-testing-patterns.md Example of a test class using its own WebApplicationFactory and Testcontainers. This provides complete isolation but can lead to higher resource consumption. ```csharp public class OrderControllerTests : IClassFixture> { private readonly OrderServiceWebApplicationFactory _factory; private readonly ITestOutputHelper _testOutput; public OrderControllerTests( OrderServiceWebApplicationFactory factory, ITestOutputHelper testOutput) { _factory = factory; _testOutput = testOutput; } [Fact] public async Task CreateOrder_ShouldReturnCreatedOrder() { // This test class has its own containers using var client = _factory.CreateClient(); // Test implementation... } } ``` -------------------------------- ### Test Order Event Consumption and Processing Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step5-write-async-tests.md This C# test class verifies that the OrderEventConsumerHostedService correctly consumes, deserializes, and processes order events from Kafka. It manually starts the consumer and polls for order processing results. ```csharp public class OrderEventListenerTests : BaseIntegrationTest { private readonly ITestOutputHelper TestOutputHelper; public OrderEventListenerTests( ITestOutputHelper testOutputHelper, OrderServiceWebApplicationFactory factory) : base(factory) { TestOutputHelper = testOutputHelper; } [Fact] public async Task TestEventIsConsumedAndProcessedByService() { // Arrange const string expectedOrderId = "123-456-789"; const string expectedCustomerId = "lbroudoux"; const int expectedProductCount = 2; // Start the HostedService manually for the test using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var consumerTask = StartOrderEventConsumerAsync(cts.Token); OrderModel? order = null; // Act & Assert - Polling pattern similar to the Java example try { order = await PollForOrderProcessingAsync(expectedOrderId, cts); Assert.NotNull(order); // Verify the order properties match expected values Assert.Equal(expectedCustomerId, order.CustomerId); Assert.Equal(OrderStatus.Validated, order.Status); Assert.Equal(expectedProductCount, order.ProductQuantities.Count); } catch (TimeoutException) { Assert.Fail("The expected Order was not received/processed in expected delay"); } finally { // Stop the consumer cts.Cancel(); await consumerTask; } } private async Task PollForOrderProcessingAsync( string expectedOrderId, CancellationTokenSource cts) { var pollDelay = TimeSpan.FromMilliseconds(400); var pollInterval = TimeSpan.FromMilliseconds(400); var startTime = DateTime.UtcNow; var orderUseCase = Factory.Services.GetRequiredService(); // Initial delay await Task.Delay(pollDelay, TestContext.Current.CancellationToken); while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(4)) { try { var order = await orderUseCase.GetOrderAsync(expectedOrderId, TestContext.Current.CancellationToken); if (order is not null) { TestOutputHelper.WriteLine($"Order {order.Id} successfully processed!"); cts.Cancel(); // Cancel the consumer after successful processing return order; } } catch (OrderNotFoundException) { // Continue polling until timeout TestOutputHelper.WriteLine($"Order {expectedOrderId} not found yet, continuing to poll..."); } await Task.Delay(pollInterval, TestContext.Current.CancellationToken); } throw new TimeoutException($"Order {expectedOrderId} was not processed within the expected timeout."); } private async Task StartOrderEventConsumerAsync(CancellationToken cancellationToken) { // Create the OrderEventConsumer manually var logger = Factory.Services.GetRequiredService>(); var orderEventConsumer = new OrderEventConsumerHostedService(logger, Factory.Services); TestOutputHelper.WriteLine("Starting OrderEventConsumer for test"); try { await orderEventConsumer.StartAsync(cancellationToken); // Keep it running until cancelled var tcs = new TaskCompletionSource(); using var registration = cancellationToken.Register(() => tcs.SetResult(true)); await tcs.Task; } finally { await orderEventConsumer.StopAsync(CancellationToken.None); orderEventConsumer.Dispose(); TestOutputHelper.WriteLine("OrderEventConsumer stopped"); } } } ``` -------------------------------- ### Check Mock Service Invocations with GetServiceInvocationsCountAsync Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md Utilize `GetServiceInvocationsCountAsync()` to get the exact number of times a mock service has been invoked. This allows for precise validation of client interactions, especially when dealing with caching or multiple requests. ```csharp [Fact] public async Task TestPastryAPIClient_GetPastryByNameAsync() { // Arrange double initialInvocationCount = await MicrocksContainer.GetServiceInvocationsCountAsync("API Pastries", "0.0.1"); var pastryAPIClient = this.Factory.Services.GetRequiredService(); // Act & Assert : Millefeuille (disponible) var millefeuille = await pastryAPIClient.GetPastryByNameAsync("Millefeuille", TestContext.Current.CancellationToken); Assert.NotNull(millefeuille); Assert.Equal("Millefeuille", millefeuille.Name); Assert.True(millefeuille.IsAvailable()); // Act & Assert : Éclair au café (disponible) var eclairCafe = await pastryAPIClient.GetPastryByNameAsync("Eclair Cafe", TestContext.Current.CancellationToken); Assert.NotNull(eclairCafe); Assert.Equal("Eclair Cafe", eclairCafe.Name); Assert.True(eclairCafe.IsAvailable()); // Act & Assert : Éclair chocolat (indisponible) var eclairChocolat = await pastryAPIClient.GetPastryByNameAsync("Eclair Chocolat", TestContext.Current.CancellationToken); Assert.NotNull(eclairChocolat); Assert.Equal("Eclair Chocolat", eclairChocolat.Name); Assert.False(eclairChocolat.IsAvailable()); // Vérifier le nombre d'invocations double finalInvocationCount = await MicrocksContainer.GetServiceInvocationsCountAsync("API Pastries", "0.0.1"); Assert.Equal(initialInvocationCount + 3, finalInvocationCount); } ``` -------------------------------- ### Restore and Build .NET Project Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step1-getting-started.md Restore project dependencies and build the project using the .NET CLI. Ensure you are in the project's root directory. ```bash dotnet restore dotnet build ``` -------------------------------- ### Clone Microcks Testcontainers .NET Demo Project Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step1-getting-started.md Clone the project repository from GitHub to your local machine using the 'git clone' command. ```bash git clone https://github.com/microcks/microcks-testcontainers-dotnet-demo.git ``` -------------------------------- ### Custom WebApplicationFactory for Integration Tests Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md This class extends `WebApplicationFactory` to provision Kafka and Microcks containers, expose necessary ports, and configure the web host for integration testing. It ensures all required contract and collection artifacts are imported into Microcks at startup. ```csharp public class OrderServiceWebApplicationFactory : WebApplicationFactory, IAsyncLifetime where TProgram : class { // ...existing code... public async ValueTask InitializeAsync() { // Allocate a free port and expose it for container communication ActualPort = GetAvailablePort(); UseKestrel(ActualPort); // ⬅️ Here we configure Kestrel to use the allocated port // ⬇️ Here we expose the port for host communication, before starting the containers await TestcontainersSettings.ExposeHostPortsAsync(ActualPort, TestContext.Current.CancellationToken) .ConfigureAwait(true); var network = new NetworkBuilder().Build(); KafkaContainer = new KafkaBuilder() .WithImage("confluentinc/cp-kafka:7.9.0") .WithNetwork(network) .WithNetworkAliases("kafka") .WithListener("kafka:19092") .Build(); await this.KafkaContainer.StartAsync(TestContext.Current.CancellationToken) .ConfigureAwait(true); this.MicrocksContainerEnsemble = new MicrocksContainerEnsemble(network, MicrocksImage) .WithAsyncFeature() .WithMainArtifacts("resources/order-service-openapi.yaml", "resources/order-events-asyncapi.yaml", "resources/third-parties/apipastries-openapi.yaml") .WithSecondaryArtifacts("resources/order-service-postman-collection.json", "resources/third-parties/apipastries-postman-collection.json") .WithKafkaConnection(new KafkaConnection($"kafka:19092")); await this.MicrocksContainerEnsemble.StartAsync() .ConfigureAwait(true); } protected override void ConfigureWebHost(IWebHostBuilder builder) { base.ConfigureWebHost(builder); var microcksContainer = this.MicrocksContainerEnsemble.MicrocksContainer; var pastryApiEndpoint = microcksContainer.GetRestMockEndpoint("API Pastries", "0.0.1"); // Configure the factory to use the Microcks container address // Use Uri constructor to ensure proper path handling builder.UseSetting("PastryApi:BaseUrl", $"{pastryApiEndpoint}/ "); } // ...existing code... } ``` -------------------------------- ### Enhanced WebApplicationFactory for Shared Containers (Pattern 2) Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/explain-integration-testing-patterns.md An enhanced WebApplicationFactory that implements IAsyncLifetime to manage shared Testcontainers for Kafka and Microcks. It ensures single port allocation and container initialization for all tests within the shared collection. ```csharp public class OrderServiceWebApplicationFactory : WebApplicationFactory, IAsyncLifetime where TProgram : class { private static readonly SemaphoreSlim InitializationSemaphore = new(1, 1); private static bool _isInitialized; public ushort ActualPort { get; private set; } public KafkaContainer KafkaContainer { get; private set; } = null!; public MicrocksContainerEnsemble MicrocksContainerEnsemble { get; private set; } = null!; public async ValueTask InitializeAsync() { await InitializationSemaphore.WaitAsync(); try { if (_isInitialized) { TestLogger.WriteLine("[Factory] Already initialized, skipping..."); return; } TestLogger.WriteLine("[Factory] Starting initialization..."); // Single port allocation for all tests ActualPort = GetAvailablePort(); UseKestrel(ActualPort); await TestcontainersSettings.ExposeHostPortsAsync(ActualPort, TestContext.Current.CancellationToken); // Single network and containers for all tests var network = new NetworkBuilder().Build(); KafkaContainer = new KafkaBuilder() .WithImage("confluentinc/cp-kafka:7.9.0") .WithNetwork(network) .WithNetworkAliases("kafka") .Build(); await KafkaContainer.StartAsync(TestContext.Current.CancellationToken); MicrocksContainerEnsemble = new MicrocksContainerEnsemble(network, "quay.io/microcks/microcks-uber:1.13.0") .WithAsyncFeature() .WithMainArtifacts("resources/order-service-openapi.yaml") .WithKafkaConnection(new KafkaConnection("kafka:19092")); await MicrocksContainerEnsemble.StartAsync(); _isInitialized = true; TestLogger.WriteLine("[Factory] Initialization completed"); } finally { InitializationSemaphore.Release(); } } // ConfigureWebHost and DisposeAsync similar to Pattern 1 } ``` -------------------------------- ### Post Order to Mocked API Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step3-local-development.md Sends a POST request to the Order API endpoint to create a new order. This interacts with the mocked services managed by Microcks. ```shell curl -XPOST localhost:5088/api/orders -H 'Content-type: application/json' \ -d '{"customerId": "lbroudoux", "productQuantities": [{"productName": "Millefeuille", "quantity": 1}], "totalPrice": 10.1}' ==== OUTPUT ==== {"id":"4b06233e-48d0-4477-86f9-a88d92634bbe","status":0,"customerId":"lbroudoux","productQuantities":[{"productName":"Millefeuille","quantity":1}],"totalPrice":10.1}% ``` -------------------------------- ### Test Order API with Different Pastry Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step3-local-development.md Sends a POST request to the Order API with a different pastry to test Microcks' API simulations and response variations. ```shell curl -POST localhost:5088/api/orders -H 'Content-type: application/json' \ -d '{"customerId": "lbroudoux", "productQuantities": [{"productName": "Millefeuille", "quantity": 1}], "totalPrice": 5.1}' -v * Host localhost:5088 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:5088... * Connected to localhost (::1) port 5088 > POST /api/orders HTTP/1.1 > Host: localhost:5088 > User-Agent: curl/8.7.1 > Accept: */* > Content-type: application/json > Content-Length: 117 > * upload completely sent off: 117 bytes < HTTP/1.1 201 Created < Content-Type: application/json; charset=utf-8 < Date: Mon, 04 Aug 2025 22:50:50 GMT < Server: Kestrel < Location: /api/orders/ee8af10a-9740-4870-a23b-0aaa7705fdee < Transfer-Encoding: chunked < * Connection #0 to host localhost left intact {"id":"ee8af10a-9740-4870-a23b-0aaa7705fdee","status":0,"customerId":"lbroudoux","productQuantities":[{"productName":"Millefeuille","quantity":1}],"totalPrice":5.1}% ``` -------------------------------- ### Verify Mock API Endpoint Usage with MicrocksContainer.VerifyAsync Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md Use `VerifyAsync()` to confirm that a specific API mock has been invoked. This is useful for ensuring your client is correctly configured to use the mock endpoints. ```csharp [Fact] public async Task TestPastryAPIClient_ListPastriesAsync() { // Arrange var pastryAPIClient = this.Factory.Services.GetRequiredService(); // Ensure the client is registered List pastries = await pastryAPIClient.ListPastriesAsync("S", TestContext.Current.CancellationToken); Assert.Single(pastries); // Assuming there is 1 pastry in the mock data pastries = await pastryAPIClient.ListPastriesAsync("M", TestContext.Current.CancellationToken); Assert.Equal(2, pastries.Count); // Assuming there are 2 pastries in the mock pastries = await pastryAPIClient.ListPastriesAsync("L", TestContext.Current.CancellationToken); Assert.Equal(2, pastries.Count); // Assuming there is 1 pastry in the mock bool isVerified = await MicrocksContainer.VerifyAsync("API Pastries", "0.0.1"); Assert.True(isVerified, "Pastry API should be verified successfully"); } ``` -------------------------------- ### Run Postman Collection Conformance Test in C# Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md This C# code snippet demonstrates how to initiate a Postman collection conformance test using Microcks within a .NET integration test. Ensure the Microcks container is properly configured and accessible. ```csharp public class OrderControllerPostmanContractTests : BaseIntegrationTest { [Fact] public void TestPostmanCollectionContract() { // Ask for a Postman Collection script conformance to be launched. TestRequest request = new() { ServiceId = "Order Service API:0.1.0", RunnerType = TestRunnerType.POSTMAN, TestEndpoint = "http://host.testcontainers.internal:" + Port + "/api" }; var testResult = await MicrocksContainer.TestEndpointAsync(request); Assert.True(testResult.Success, "Test should be successful"); Assert.Single(testResult.TestCaseResults); } } ``` -------------------------------- ### Test HTTP API with HttpClient in C# Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md Use HttpClient to invoke an exposed HTTP API and validate responses with .NET assertions. This approach requires writing explicit assertions for each API interaction. ```csharp using Microsoft.AspNetCore.Mvc.Testing; using System.Net.Http; using System.Threading.Tasks; using Xunit; namespace Order.Service.Tests.Api; public class OrderControllerTests : IClassFixture> { private readonly HttpClient _client; public OrderControllerTests(WebApplicationFactory factory) { _client = factory.CreateClient(); } [Fact] public async Task Should_Return_Order_When_Get_By_Id() { // Arrange var orderId = 5; var response = await _client.GetAsync($"/api/orders/{orderId}"); // Assert response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); using var doc = System.Text.Json.JsonDocument.Parse(json); var root = doc.RootElement; // Vérification explicite de la structure JSON Assert.True(root.TryGetProperty("orderId", out var orderIdProp), "orderId property should exist"); Assert.Equal(orderId, orderIdProp.GetInt32()); Assert.True(root.TryGetProperty("status", out var statusProp), "status property should exist"); Assert.Equal("Created", statusProp.GetString()); // Vérifier d'autres propriétés attendues si besoin } } ``` -------------------------------- ### Order Event Publisher Contract Test for Kafka Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step5-write-async-tests.md This C# test class verifies that the Order Service correctly publishes events to a Kafka topic. It uses Microcks for contract validation and TestContainers for managing the Kafka broker. Ensure the Kafka topic exists before running the test. ```csharp public class OrderEventPublisherContractTests : BaseIntegrationTest { private readonly ITestOutputHelper TestOutputHelper; public OrderEventPublisherContractTests( ITestOutputHelper testOutputHelper, OrderServiceWebApplicationFactory factory) : base(factory) { TestOutputHelper = testOutputHelper; } [Fact] public async Task TestOrderEventIsPublishedOnKafka() { // Ensure topic exists await EnsureTopicExistsAsync("orders-created") .ConfigureAwait(true); // Prepare a Microcks test. var kafkaTest = new TestRequest { ServiceId = "Order Events API:0.1.0", FilteredOperations = new[] { "SUBSCRIBE orders-created" }, RunnerType = TestRunnerType.ASYNC_API_SCHEMA, TestEndpoint = "kafka://kafka:19092/orders-created", Timeout = 2000L }; // Prepare an application Order. var orderInfo = new OrderInfo { CustomerId = "123-456-789", ProductQuantities = new[] { new ProductQuantity("Millefeuille", 1), new ProductQuantity("Eclair Cafe", 1) }, TotalPrice = 8.4 }; // Launch the Microcks test and wait a bit to be sure it actually connects to Kafka. var testRequestTask = MicrocksContainer.TestEndpointAsync(kafkaTest, TestContext.Current.CancellationToken); await Task.Delay(750, TestContext.Current.CancellationToken); // Invoke the application to create an order. var orderUseCase = Factory.Services.GetRequiredService(); var createdOrder = await orderUseCase.PlaceOrderAsync( orderInfo, TestContext.Current.CancellationToken); // Get the Microcks test result. var testResult = await testRequestTask; // Check success and that we read 1 valid message on the topic. Assert.True(testResult.Success); Assert.NotEmpty(testResult.TestCaseResults); Assert.Single(testResult.TestCaseResults[0].TestStepResults); var events = await MicrocksContainer.GetEventMessagesForTestCaseAsync(testResult, "SUBSCRIBE orders-created", TestContext.Current.CancellationToken); Assert.Single(events); } } ``` -------------------------------- ### Pastry API Client Integration Tests Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md These tests verify the Pastry API Client's functionality by interacting with Microcks mock endpoints. Ensure the PastryAPIClient is registered in the service collection. ```csharp public class PastryAPIClientTests : BaseIntegrationTest { private readonly ITestOutputHelper TestOutputHelper; public PastryAPIClientTests( ITestOutputHelper testOutputHelper, OrderServiceWebApplicationFactory factory) : base(factory) { TestOutputHelper = testOutputHelper; } [Fact] public async Task TestPastryAPIClient_ListPastriesAsync() { // Arrange var pastryAPIClient = this.Factory.Services.GetRequiredService(); // Ensure the client is registered List pastries = await pastryAPIClient.ListPastriesAsync("S", TestContext.Current.CancellationToken); Assert.Single(pastries); // Assuming there is 1 pastry in the mock data pastries = await pastryAPIClient.ListPastriesAsync("M", TestContext.Current.CancellationToken); Assert.Equal(2, pastries.Count); // Assuming there are 2 pastries in the mock pastries = await pastryAPIClient.ListPastriesAsync("L", TestContext.Current.CancellationToken); Assert.Equal(2, pastries.Count); // Assuming there is 1 pastry in the mock } [Fact] public async Task TestPastryAPIClient_GetPastryByNameAsync() { // Arrange var pastryAPIClient = this.Factory.Services.GetRequiredService(); // Act & Assert : Millefeuille (disponible) var millefeuille = await pastryAPIClient.GetPastryByNameAsync("Millefeuille", TestContext.Current.CancellationToken); Assert.NotNull(millefeuille); Assert.Equal("Millefeuille", millefeuille.Name); Assert.True(millefeuille.IsAvailable()); // Act & Assert : Éclair au café (disponible) var eclairCafe = await pastryAPIClient.GetPastryByNameAsync("Eclair Cafe", TestContext.Current.CancellationToken); Assert.NotNull(eclairCafe); Assert.Equal("Eclair Cafe", eclairCafe.Name); Assert.True(eclairCafe.IsAvailable()); // Act & Assert : Éclair chocolat (indisponible) var eclairChocolat = await pastryAPIClient.GetPastryByNameAsync("Eclair Chocolat", TestContext.Current.CancellationToken); Assert.NotNull(eclairChocolat); Assert.Equal("Eclair Chocolat", eclairChocolat.Name); Assert.False(eclairChocolat.IsAvailable()); } } ``` -------------------------------- ### Base Integration Test Class in C# Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md This class serves as a base for integration tests, implementing IClassFixture to share a WebApplicationFactory. It provides an HttpClient configured with the factory's settings. ```csharp using System.Net.Http; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; namespace Order.Service.Tests { public class BaseIntegrationTest : IClassFixture> { protected readonly HttpClient _client; public BaseIntegrationTest(WebApplicationFactory factory) { _client = factory.CreateClient(); } } } ``` -------------------------------- ### Base Integration Test Class Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/explain-integration-testing-patterns.md Abstract base class for integration tests using Microcks Testcontainers. It sets up shared resources like WebApplicationFactory, MicrocksContainerEnsemble, and KafkaContainer. ```csharp [Collection(SharedTestCollection.Name)] public abstract class BaseIntegrationTest { public WebApplicationFactory Factory { get; private set; } public ushort Port { get; private set; } public MicrocksContainerEnsemble MicrocksContainerEnsemble { get; } public KafkaContainer KafkaContainer { get; } public HttpClient HttpClient { get; private set; } protected BaseIntegrationTest(OrderServiceWebApplicationFactory factory) { Factory = factory; HttpClient = factory.CreateClient(); Port = factory.ActualPort; MicrocksContainerEnsemble = factory.MicrocksContainerEnsemble; KafkaContainer = factory.KafkaContainer; } protected void SetupTestOutput(ITestOutputHelper testOutputHelper) { TestLogger.SetTestOutput(testOutputHelper); } } ``` -------------------------------- ### Order Controller Test Class Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/explain-integration-testing-patterns.md Concrete implementation of an integration test class for the OrderController. It inherits from BaseIntegrationTest and sets up test output logging. ```csharp public class OrderControllerTests : BaseIntegrationTest { private readonly ITestOutputHelper _testOutput; public OrderControllerTests( ITestOutputHelper testOutput, OrderServiceWebApplicationFactory factory) : base(factory) { _testOutput = testOutput; SetupTestOutput(testOutput); } [Fact] public async Task CreateOrder_ShouldReturnCreatedOrder() { // Shared containers with all other test classes // Test implementation... } } ``` -------------------------------- ### Test Unavailable Pastry Request Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step3-local-development.md Sends a POST request for an unavailable pastry to trigger a specific error response simulated by Microcks. ```shell curl -POST localhost:5088/api/orders -H 'Content-type: application/json' \ -d '{"customerId": "lbroudoux", "productQuantities": [{"productName": "Eclair Chocolat", "quantity": 1}], "totalPrice": 4.1}' -v * Host localhost:5088 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:5088... * Connected to localhost (::1) port 5088 > POST /api/orders HTTP/1.1 > Host: localhost:5088 > User-Agent: curl/8.7.1 > Accept: */* > Content-type: application/json > Content-Length: 120 > * upload completely sent off: 120 bytes < HTTP/1.1 422 Unprocessable Entity < Content-Type: application/json; charset=utf-8 < Date: Mon, 04 Aug 2025 22:54:57 GMT < Server: Kestrel < Transfer-Encoding: chunked < * Connection #0 to host localhost left intact {"productName":"Eclair Chocolat","details":"Pastry 'Eclair Chocolat' is unavailable."}% ``` -------------------------------- ### Verify Event Content with Microcks Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step5-write-async-tests.md Use GetEventMessagesForTestCaseAsync to retrieve and assert the content of emitted Kafka events. This requires the Microcks container to have captured the event during the test. ```csharp [Fact] public async Task TestOrderEventIsPublishedOnKafka() { // [...] Unchanged comparing previous step. // Get the Microcks test result. var testResult = await testRequestTask; // [...] Unchanged comparing previous step. // Check the content of the emitted event, read from Kafka topic. var events = await MicrocksContainer.GetEventMessagesForTestCaseAsync(testResult, "SUBSCRIBE orders-created", TestContext.Current.CancellationToken); Assert.Single(events); var message = events[0].EventMessage; var messageMap = JsonSerializer.Deserialize>(message.Content); // Properties from the event message should match the order. Assert.Equal("Creation", messageMap["changeReason"].GetString()); var orderMap = JsonSerializer.Deserialize>(messageMap["order"].GetRawText()); Assert.Equal("123-456-789", orderMap["customerId"].GetString()); Assert.Equal(8.4, orderMap["totalPrice"].GetDouble()); Assert.Equal(2, orderMap["productQuantities"].GetArrayLength()); } ``` -------------------------------- ### Validate Order Products and Quantities with Postman Source: https://github.com/microcks/microcks-testcontainers-dotnet-demo/blob/main/step4-write-rest-tests.md This Postman script tests if the order response contains the correct products and quantities. It's typically used within Microcks to enforce business constraints on API responses. ```javascript pm.test("Correct products and quantities in order", function () { var order = pm.response.json(); var productQuantities = order.productQuantities; pm.expect(productQuantities).to.be.an("array"); pm.expect(productQuantities.length).to.eql(requestProductQuantities.length); for (let i=0; i> { public const string Name = "SharedTestCollection"; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.