Aspire extension that enables hosting Microcks as a service, managing mocks for dependencies and contract-testing of your API endpoints in your .NET App.
Current development version is 0.1.0.
- Documentation
- Microcks Community and community meeting
- Join us on Discord, on GitHub Discussions or CNCF Slack #microcks channel
To get involved with our community, please make sure you are familiar with the project's Code of Conduct.
dotnet add package Microcks.Aspire --version 0.1.0
With Aspire, you add Microcks as a resource to your distributed application. This library requires a Microcks uber distribution (with no MongoDB dependency).
In your AppHost Program.cs:
var microcks = builder.AddMicrocks("microcks")
.WithImage("microcks/microcks-uber", "latest");To use Microcks mocks or contract-testing features, you first need to import OpenAPI, Postman Collection, GraphQL or gRPC artifacts. Artifacts can be imported as main/Primary ones or as secondary ones. See Multi-artifacts support for details.
With Aspire, you import artifacts directly when adding the Microcks resource to your distributed application:
var microcks = builder.AddMicrocks("microcks")
.WithMainArtifacts(
"resources/third-parties/apipastries-openapi.yaml",
"resources/order-service-openapi.yaml"
)
.WithSecondaryArtifacts(
"resources/order-service-postman-collection.json",
"resources/third-parties/apipastries-postman-collection.json"
);You can also import full repository snapshots at once:
var microcks = builder.AddMicrocks("microcks")
.WithSnapshots("microcks-repository.json");During your test setup, you'd probably need to retrieve mock endpoints provided by Microcks to setup your base API url calls. With Aspire, you can do it like this:
// From the MicrocksResource directly
var pastryApiUrl = microcksResource
.GetRestMockEndpoint("API Pastries", "0.0.1")
.ToString();The MicrocksResource provides methods for different supported API styles/protocols (Soap, GraphQL, gRPC,...).
You can then use these endpoints to configure your HTTP clients in tests:
var webApplicationFactory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.UseEnvironment("Test");
builder.UseSetting("PastryApi:BaseUrl", pastryApiUrl);
});Once the mock endpoint has been invoked, you'd probably need to ensure that the mock have been really invoked.
With Aspire, you can do it like this using the MicrocksClient:
var app = orderHostAspireFactory.App;
var microcksClient = app.CreateMicrocksClient("microcks");
bool isVerified = await microcksClient.VerifyAsync(
"API Pastries", "0.0.1",
cancellationToken: TestContext.Current.CancellationToken);
Assert.True(isVerified, "API should be verified successfully");Or check the invocations count like this:
double initialCount = await microcksClient.GetServiceInvocationsCountAsync(
"API Pastries", "0.0.1",
cancellationToken: TestContext.Current.CancellationToken);
// ... perform your API calls ...
double finalCount = await microcksClient.GetServiceInvocationsCountAsync(
"API Pastries", "0.0.1",
cancellationToken: TestContext.Current.CancellationToken);
Assert.Equal(initialCount + expectedCalls, finalCount);If you want to ensure that your application under test is conformant to an OpenAPI contract (or other type of contract), you can launch a Microcks contract/conformance test using Aspire's distributed application model and MicrocksClient integration.
Here is an example using Aspire and the Microcks extension, fully aligned with the test code:
You can use service discovery for endpoint resolution (note: endpoint name is case-sensitive and must match your configuration, e.g., "order-api" or "Order-Api"):
[Fact]
public async Task TestOpenApiContract_WithServiceDiscovery()
{
var app = orderHostAspireFactory.App;
TestRequest request = new()
{
ServiceId = "Order Service API:0.1.0",
RunnerType = TestRunnerType.OPEN_API_SCHEMA,
TestEndpoint = $"http://order-api:3002/api" // Container Host + Target Port
};
var microcksClient = app.CreateMicrocksClient("microcks");
var testResult = await microcksClient.TestEndpointAsync(request, TestContext.Current.CancellationToken);
var json = JsonSerializer.Serialize(testResult, new JsonSerializerOptions { WriteIndented = true });
testOutputHelper.WriteLine(json);
Assert.True(testResult.Success);
Assert.False(testResult.InProgress, "Test should not be in progress");
Assert.Single(testResult.TestCaseResults);
}And here is the same example but using direct host access (note: ensure that your Microcks instance can access the host.docker.internal address):
[Fact]
public async Task TestOpenApiContract()
{
// Arrange
var app = orderHostAspireFactory.App;
int port = app.GetEndpoint("order-api").Port; // Get the mapped port
// Act
TestRequest request = new()
{
ServiceId = "Order Service API:0.1.0",
RunnerType = TestRunnerType.OPEN_API_SCHEMA,
TestEndpoint = $"http://host.docker.internal:{port}/api"
};
var microcksClient = app.CreateMicrocksClient("microcks");
var testResult = await microcksClient.TestEndpointAsync(request, TestContext.Current.CancellationToken);
// Assert
// You may inspect complete response object with following:
var json = JsonSerializer.Serialize(testResult, new JsonSerializerOptions { WriteIndented = true });
testOutputHelper.WriteLine(json);
Assert.True(testResult.Success);
Assert.False(testResult.InProgress, "Test should not be in progress");
Assert.Single(testResult.TestCaseResults);
}You can retrieve and inspect exchanged messages for advanced business conformance validation. Here is an example that matches the test code:
var messages = await microcksClient.GetMessagesForTestCaseAsync(testResult, "POST /orders", TestContext.Current.CancellationToken);
foreach (var message in messages)
{
if ("201".Equals(message.Response.Status))
{
var responseDocument = JsonDocument.Parse(message.Response.Content);
var requestDocument = JsonDocument.Parse(message.Request.Content);
var requestProductQuantities = requestDocument.RootElement.GetProperty("productQuantities");
var responseProductQuantities = responseDocument.RootElement.GetProperty("productQuantities");
Assert.Equal(requestProductQuantities.GetArrayLength(), responseProductQuantities.GetArrayLength());
for (int i = 0; i < requestProductQuantities.GetArrayLength(); i++)
{
var reqProductName = requestProductQuantities[i].GetProperty("productName").GetString();
var respProductName = responseProductQuantities[i].GetProperty("productName").GetString();
Assert.Equal(reqProductName, respProductName);
}
}
}Note:
- With Aspire, endpoint discovery and Microcks integration are handled automatically. You do not need to manually expose ports or refactor your hosting model as with classic Testcontainers usage.
- Always ensure endpoint names (e.g., "order-api" or "Order-Api") match your distributed application configuration.
- Use
TestContext.Current.CancellationTokenfor async test cancellation, and consider logging the serialized test result for easier debugging.
The Microcks Aspire extension supports essential features of Microcks:
- Mocking of REST APIs using different kinds of artifacts,
- Contract-testing of REST APIs using
OPEN_API_SCHEMArunner/strategy, - Mocking and contract-testing of SOAP WebServices,
- Mocking and contract-testing of GraphQL APIs,
- Mocking and contract-testing of gRPC APIs.
With Aspire, all these features are automatically available when you add the Microcks resource to your distributed application. The integration handles the orchestration and networking automatically.
You can configure additional features directly in your AppHost, to support more advanced use-cases like AsyncAPI contract-testing or Postman contract-testing, we introduced dedicated extension methods for the MicrocksBuilder that allow managing additional Microcks services.
Different levels of API contract testing in the Inner Loop with Testcontainers and available in Microcks .NET Aspire!
var microcks = builder.AddMicrocks("microcks")
.WithMainArtifacts("pastry-orders-asyncapi.yml")
// Additional configuration can be added here
.WithPostmanRunner(); // Enables Postman contract-testing supportFor Postman contract-testing, you can use the same TestRequest pattern with the MicrocksClient:
var microcksClient = app.CreateMicrocksClient("microcks");
var testRequest = new TestRequest
{
ServiceId = "API Pastries:0.0.1",
RunnerType = TestRunnerType.POSTMAN,
TestEndpoint = $"{app.GetEndpoint("my-api")}/api",
Timeout = TimeSpan.FromSeconds(3)
};
TestResult testResult = await microcksClient.TestEndpointAsync(testRequest, TestContext.Current.CancellationToken);If you want to configure Postman runner specific settings, you can do it when adding the Postman runner to the Microcks resource:
var microcks = builder.AddMicrocks("microcks")
.WithPostmanRunner(postmanBuilder =>
{
// Customize Postman runner settings
postmanBuilder.WithEnvironment("MY_ENV_VAR", "sample");
});Note: With Aspire, the complexity of managing multiple containers and networks is abstracted away. The distributed application model handles service discovery, networking, and lifecycle management automatically.
Async APIs can also be mocked and contract-tested using Microcks with Aspire. You can import AsyncAPI artifacts and use the MicrocksClient to validate message exchanges.
This feature requires to be explicitly enabled when adding the Microcks resource.
It's done by calling the WithAsyncFeature() method as shown below:
var kafkaBuilder = Builder.AddKafka("kafka")
.WithKafkaUI();
var microcksBuilder = builder.AddMicrocks("microcks")
.WithMainArtifacts("pastry-orders-asyncapi.yml")
.WithAsyncFeature(minion => {
minion.WithKafkaConnection(kafkaBuilder, 9093);
});As you can see, we inject the KafkaBuilder into the minion to ensure it can connect properly.
Once started, you can retrieve mock topics from the MicrocksAsyncMinionResource for different
supported protocols (WebSocket, Kafka, etc.). Here's how to consume messages sent by the Microcks Async Minion:
// Retrieve MicrocksAsyncMinionResource from application
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var microcksAsyncMinionResource = appModel.GetContainerResources()
.OfType<MicrocksAsyncMinionResource>()
.Single();
// Get the Kafka topic for mock messages
string kafkaTopic = microcksAsyncMinionResource
.GetKafkaMockTopic("Pastry orders API", "0.1.0", "SUBSCRIBE pastry/orders");
// Subscribe to the topic and consume messages
consumer.Subscribe(kafkaTopic);
var consumeResult = consumer.Consume(TimeSpan.FromMilliseconds(5000));Using contract-testing techniques on Asynchronous endpoints may require a different style of interacting with the Microcks container. For example, you may need to:
- Start the test making Microcks listen to the target async endpoint,
- Activate your System Under Tests so that it produces an event,
- Finalize the Microcks tests and actually ensure you received one or many well-formed events.
For that you can use the MicrocksClient which provides a TestEndpointAsync(TestRequest request) method that returns a Task<TestResult>.
Once invoked, you may trigger your application events and then await the future result to assert like this:
var testRequest = new TestRequest
{
ServiceId = "Pastry orders API:0.1.0",
RunnerType = TestRunnerType.ASYNC_API_SCHEMA,
TestEndpoint = "kafka://kafka:9093/pastry-orders",
Timeout = TimeSpan.FromSeconds(5)
};
var microcksClient = app.CreateMicrocksClient("microcks");
// Start the test, making Microcks listen to the endpoint
var taskTestResult = microcksClient.TestEndpointAsync(testRequest, cancellationToken);
// Wait a bit to let the test initialize
await Task.Delay(750, cancellationToken);
// Produce your events to the Kafka topic
producer.Produce("pastry-orders", new Message<string, string>
{
Key = Guid.NewGuid().ToString(),
Value = yourMessage
});
// Now retrieve the final test result and assert
TestResult testResult = await taskTestResult;
Assert.True(testResult.Success);In addition, you can use the GetEventMessagesForTestCaseAsync() method to retrieve the events received during the test. This is particularly useful for inspecting message content and validating business logic:
// Retrieve event messages for the failing test case
List<UnidirectionalEvent> events = await microcksClient.GetEventMessagesForTestCaseAsync(
testResult, "SUBSCRIBE pastry/orders", TestContext.Current.CancellationToken);
// Inspect the events
Assert.True(events.Count >= 1);
// Check event message content
foreach (var eventItem in events)
{
Assert.NotNull(eventItem.EventMessage);
var messageContent = eventItem.EventMessage.Content;
// Parse and validate message structure
var jsonDocument = JsonDocument.Parse(messageContent);
var root = jsonDocument.RootElement;
// Validate required fields
Assert.True(root.TryGetProperty("id", out _));
Assert.True(root.TryGetProperty("customerId", out _));
Assert.True(root.TryGetProperty("productQuantities", out _));
}This allows developers to perform detailed validation of the async messages exchanged during contract testing.