Developing and Testing Spring Boot Apps with Azure Storage locally

Developing and Testing Spring Boot Apps with Azure Storage locally

Use Azurite and TestContainers locally to emulate parts of the Azure Cloud

Azure Storage is the Microsoft equivalent of Amazon's S3 (Simple Storage Service). Its blob storage stores any type of text or binary data, such as a document, media file, or application installer.

Microsoft provides a great client library for Spring Boot applications to access Azure Storage.

All you need to do is to add the following dependency:

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>azure-spring-boot-starter-storage</artifactId>
   <version>3.10.0</version>
</dependency>

Then enter your account name, account key, and blob-endpoint to your application.yaml - you get all of these values from the Azure Portal:

azure:
  storage: 
    account-name: <storage-account-name>
    account-key: <storage-account-access-key>
    blob-endpoint: <storage-endpoint-URL>

You can then autowire beans like the BlobServiceClientBuilder which allow to access your storage and upload or download files, as explained in detail in the client library.

While this setup is easy to use it has one major drawback: It makes you dependent on Azure Storage at development and integration test time, which means without access to the internet you can't develop anymore, and if your integration tests depend on Azure as well you even can't run your build without being online. While especially in cloud native applications this will be more and more the case, especially when it comes to sophisticated AI services, I believe that for fundamental services like storing files on a blob storage you should make yourself independent as far as you can.

Fortunately, there is a solution:

Azurite as local Azure Storage

Just like there is MinIO for AWS S3, there is Azurite for Azure Storage.

Azurite is an open source Azure Storage API compatible server (emulator). Based on Node.js, Azurite provides cross platform experiences for customers wanting to try Azure Storage easily in a local environment. Azurite simulates most of the commands supported by Azure Storage with minimal dependencies.

While Azurite supports wide parts of the Azure Storage API (blob storage, queue storage, table storage), it's still not the same as the original part on Azure, which reminds me of my previous blog entry where I've written about MockMvc in Spring as a replacement for a full-blown web server.

Anyways, for most of the use cases, Azurite is more than good enough, just like MinIO as a mature local replacement for Amazon's S3.

Starting Azurite locally can be done with a one-liner via Docker:

docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite

Now change your application.yaml from above to the following values:

azure:
  storage: 
    account-name: devstoreaccount1
    account-key: Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
    blob-endpoint: http://localhost:10000/devstoreaccount1

Restart your application, and you will be connected to your local Azurite instance and can act fully autonomously.

Using Playtika TestContainers for local integration testing

One of the great things with Docker is TestContainers which allows a seamless integration with JUnit5 tests.

Playtikas testcontainers-spring-boot library is a wrapper around TestContainers which integrates well with Spring Boot`s auto-configuration concept. It preconfigures your beans and takes care of dynamic ports to not interfere with other local containers.

It provides modules for a lot of containers like MySQL, RabbitMQ or ElasticSearch, but there was no support yet for Azurite, so I implemented that on my own and created a pull request which was merged and published very soon.

Support for testing Azurite via Testcontainers and Spring Boot is now available to everyone, and this is how it works:

Import that library first:

<dependency>
    <groupId>com.playtika.testcontainers</groupId>
    <artifactId>embedded-azurite</artifactId>
    <scope>test</scope>
</dependency>

In your src/test/resources/application.yaml, add the following properties:

azure:
  storage:
    account-name: ${embedded.azurite.account-name}
    account-key: ${embedded.azurite.account-key}
    blob-endpoint: ${embedded.azurite.blob-endpoint}

Those embedded.azurite.* properties are being produced by embedded-azurite as explained in detail in the docu.

Last but not least, create a @SpringBootTest which adds @EnableAutoConfiguration, you can then autowire beans like the BlobServiceClientBuilder, and Testcontainers will take care of starting Azurite before your test is being executed.

The test class of embedded-azurite shows you an example for that.

@SpringBootTest(classes = EmbeddedAzuriteBoostrapConfigurationTest.AzuriteTestConfiguration.class)
class EmbeddedAzuriteBoostrapConfigurationTest {

    @Autowired
    BlobServiceClientBuilder blobServiceClientBuilder;

    @Test
    void accountName() {
        BlobServiceClient blobServiceClient = blobServiceClientBuilder
             .buildClient();

        assertThat(blobServiceClient.getAccountName())
            .isEqualTo(AzuriteContainer.ACCOUNT_NAME);
    }

    @EnableAutoConfiguration
    public static class AzuriteTestConfiguration {
    }
}

Now you can not only develop locally but also run integration tests with Testcontainers locally without being dependent on any external service.