/in-memory-azure-test-sdk

Drop-in fakes of Azure .NET SDKs to make your test blazing-fast and reliable.

Primary LanguageC#MIT LicenseMIT

Azure In-Memory SDKs for Testing

Drop-in fakes of Azure .NET SDKs to make your test blazing-fast and reliable.

Supported SDKs | Example | Key features | Why Should I Use It? | How It Works | License

CI status

Supported SDKs

Package Relevant Azure SDK NuGet
Spotflow.InMemory.Azure.Storage Azure.Storage.Blobs, Azure.Data.Tables NuGet
Spotflow.InMemory.Azure.Storage.FluentAssertions NuGet
Spotflow.InMemory.Azure.EventHubs Azure.Messaging.EventHubs NuGet
Spotflow.InMemory.Azure.ServiceBus Azure.Messaging.ServiceBus NuGet
Spotflow.InMemory.Azure.ServiceBus.FluentAssertions NuGet
Spotflow.InMemory.Azure.KeyVault Azure.Security.KeyVault.Secrets NuGet

Example

See how the in-memory Azure SDKs can be used in your code, for example with Azure Storage:

Design

var storageAccount = new InMemoryStorageProvider().AddAccount();

// The InMemoryBlobContainerClient inherits from BlobContainerClient (from the official SDK)
// So it can be used as a drop-in replacement for the real BlobContainerClient in your tests
var containerClient = InMemoryBlobContainerClient.FromAccount(storageAccount, "test-container");

// From now on, you can use the BlobContainerClient methods as you're used to:
containerClient.Create();

await containerClient.UploadBlobAsync("my-blob", BinaryData.FromString("Hello World!"));

// The `containerClient` can now be used in your code as if it was a real BlobContainerClient:

await PrintBlobAsync(containerClient);

async Task PrintBlobAsync(BlobContainerClient container)
{
    var blob = container.GetBlobClient("my-blob");

    var response = await blob.DownloadContentAsync();

    Console.WriteLine(response.Value.Content.ToString());
    // Output: Hello World!
}

This design allows you to create a factory for SDK clients with two implementations: one that provides the official Azure SDK clients and another that provides in-memory clients. By selecting the appropriate factory, you can use the real implementation in your production code and the in-memory implementation in your tests.

You can learn how we recommend to use this library in the documentations for each SDK.

Key Features

  • Drop-in Replacement of the official Azure SDKs.
  • Blazing-fast thanks to the in-memory implementation.
  • No external dependencies, not even Docker.
  • Fault Injection: build resilient code thanks to simulated Azure outages in your tests.
  • Delay Injection: Examine behavior of your system under pressure of slowed-down operations.
  • TimeProvider Support: avoid flaky tests thanks to the time abstraction.
  • Fluent Assertions to conveniently test common scenarios.
  • Customizable: you can easily extend the functionality of the in-memory providers via before and after hooks.

Why Should I Use It?

There's been a lot written on why to prefer fakes over mocks in tests. Mocks are test-doubles that return pre-programmed responses to method calls. This can tightly couple your tests to implementation details, making them brittle and hard to maintain. Fakes, on the other hand, are lightweight implementations of real services that can seamlessly integrate into your tests. Using real services in tests is another approach, which is reasonable in many cases but can result in tests that are slow and harder to manage.

One major drawback of fakes is the initial effort required to create them. We have solved this problem by implementing them for you. This way, you can use the same interfaces and methods as in the real SDKs, but with the benefits of in-memory implementation.

How It Works

The Azure SDKs are designed for inheritance-based testability:

  • Important methods are virtual.
  • There are parameterless protected constructor available for all clients.
  • There are static factories for creating most models.

The in-memory clients (e.g. InMemoryBlobContainerClient or InMemoryEventHubConsumerClient) provided by this library are inheriting the Azure SDK clients so that they can be injected to any code that expected the actual Azure SDK clients (the BlobContainerClient or EventHubConsumerClient the previous example). The tested code can therefore depend directly on Azure SDK clients and only abstract away creation of these clients. This removes the need to design and implement custom client interfaces.

The in-memory clients have similar constructors as real clients but they all also require a so-called in-memory provider (e.g. InMemoryStorageProvider or InMemoryEventHubProvider). The in-memory providers emulate the functionality of the actual services for which the SDK clients are created for (e.g. Azure Storage or Azure Event Hubs). The providers allows to read, change and assert the internal state during testing. For most Azure SDK clients, the in-memory providers are exposing corresponding types representing actual state. For example for InMemoryBlobContainerClient: BlobContainerClient, there is InMemoryBlobContainer type exposed by the provider. The important difference is that the InMemoryBlobContainer is representing the actual state (in this case an existing Azure Storage Blob Container) while InMemoryBlobContainerClient might be representing container that does not yet exist.

Maintainers

Contributing

Please read our Contributing Guidelines to learn how you can contribute to this project.

License

This project is licensed under the MIT license.