DEV Community 👩‍💻👨‍💻

Franz Wong
Franz Wong

Posted on

Mock S3 for integration test

Although we can use S3 mock library (e.g. adobe/S3Mock), sometimes we just want to make thing simple.

The full source code is put at the end of this page.

Add 3rd party dependencies

Undertow is used as S3 mock server. AWS SDK v2 is used for S3 client.

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-core</artifactId>
    <version>2.2.17.Final</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.17.247</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>url-connection-client</artifactId>
    <version>2.17.247</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.9.0</version>
    <scope>test</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Create S3 mock server

First we create a HttpHandler. It will return the content of S3 object.

static class S3HttpHandler implements HttpHandler {

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        if (exchange.isInIoThread()) {
            exchange.dispatch(this);
            return;
        }

        System.out.printf("Request method: %s%n", exchange.getRequestMethod());
        System.out.printf("Request path: %s%n", exchange.getRequestPath());

        try (BlockingHttpExchange blockingExchange = exchange.startBlocking();
             OutputStream outputStream = exchange.getOutputStream();
             OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
             BufferedWriter writer = new BufferedWriter(outputStreamWriter)) {

            writer.write("Hello world");
        }
    }

}
Enter fullscreen mode Exit fullscreen mode

Then we start the mock server with our HttpHandler.

final int S3_MOCK_SERVER_PORT = 9999;

Undertow s3Server = Undertow.builder()
    .addHttpListener(S3_MOCK_SERVER_PORT, "0.0.0.0")
    .setHandler(new S3HttpHandler())
    .build();
s3Server.start();
Enter fullscreen mode Exit fullscreen mode

Don't forget to stop our server at the end.

s3Server.stop();
Enter fullscreen mode Exit fullscreen mode

Setup S3 client

We need to create a S3Client with the service endpoint pointing to our S3 mock server.

String serviceEndpoint = String.format("http://localhost:%d", S3_MOCK_SERVER_PORT);

try (S3Client s3Client = S3Client.builder()
    .endpointOverride(URI.create(serviceEndpoint))
    .httpClient(UrlConnectionHttpClient.builder().build())
    .build()) {

}
Enter fullscreen mode Exit fullscreen mode

Send a request to the mock server.

GetObjectRequest request = GetObjectRequest.builder()
    .bucket("my-bucket")
    .key("my-object")
    .build();

Path targetPath = tempDir.resolve("result");

s3Client.getObject(request, targetPath);

String objectContent = Files.readString(targetPath);
System.out.printf("Object content: %s%n", objectContent);
assertEquals("Hello world", objectContent);
Enter fullscreen mode Exit fullscreen mode

Full source code

public class S3IntegrationTest {

    @Test
    public void shouldReturnS3Object(@TempDir Path tempDir) throws Exception {
        final int S3_MOCK_SERVER_PORT = 9999;

        Undertow s3Server = Undertow.builder()
            .addHttpListener(S3_MOCK_SERVER_PORT, "0.0.0.0")
            .setHandler(new S3HttpHandler())
            .build();

        s3Server.start();

        String serviceEndpoint = String.format("http://localhost:%d", S3_MOCK_SERVER_PORT);

        try (S3Client s3Client = S3Client.builder()
            .endpointOverride(URI.create(serviceEndpoint))
            .httpClient(UrlConnectionHttpClient.builder().build())
            .build()) {

            GetObjectRequest request = GetObjectRequest.builder()
                .bucket("my-bucket")
                .key("my-object")
                .build();

            Path targetPath = tempDir.resolve("result");

            s3Client.getObject(request, targetPath);

            String objectContent = Files.readString(targetPath);
            System.out.printf("Object content: %s%n", objectContent);
            assertEquals("Hello world", objectContent);
        }

        s3Server.stop();
    }

    static class S3HttpHandler implements HttpHandler {

        @Override
        public void handleRequest(HttpServerExchange exchange) throws Exception {
            if (exchange.isInIoThread()) {
                exchange.dispatch(this);
                return;
            }

            System.out.printf("Request method: %s%n", exchange.getRequestMethod());
            System.out.printf("Request path: %s%n", exchange.getRequestPath());

            try (BlockingHttpExchange blockingExchange = exchange.startBlocking();
                 OutputStream outputStream = exchange.getOutputStream();
                 OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                 BufferedWriter writer = new BufferedWriter(outputStreamWriter)) {

                writer.write("Hello world");
            }
        }

    }

}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
marcuswong profile image
marcuswong

For development, can also consider using minio
docs.min.io/docs/minio-quickstart-...

🌚 Browsing with dark mode makes you a better developer by a factor of exactly 40.

It's a scientific fact.