Faked out DynamoDBMapper

Wanna try DynamoDB without using your network interface ?
Not requiring connection will allow any developer on any platform to run your tests without setup pain or docker installation.

Below is a mapper able to do the two most basic operations necessary to read and write data : save and query.
You will be able to fake out the database and to concentrate on the business logic in your tests.
Who wants to use mocked behaviors with dynamo when you can just use things like … HashMaps ?

import com.amazonaws.services.dynamodbv2.datamodeling.*;
import com.amazonaws.services.dynamodbv2.model.QueryRequest;
import com.amazonaws.services.dynamodbv2.model.QueryResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Arrays.stream;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

class MockDynamoConfiguration {

    public DynamoDBMapper mockedDynamoDBMapper() {
        return new DynamoDBMapper(null) {
            private Map, Set> tables = new HashMap();

            public <T> void save(T object) {
                tables.computeIfAbsent(object.getClass().getName(), k -> new HashSet()).add(object);

            public <T> PaginatedQueryList<T> query(Class<T> clazz, DynamoDBQueryExpression<T> queryExpression) {
                T candidate = queryExpression.getHashKeyValues();
                Object hashKey = findHashKey(candidate, queryExpression);
                T foundElement = findElement(clazz, hashKey, queryExpression.getIndexName());
                List<T> results = foundElement == null ? emptyList() :
                        singletonList(findElement(clazz, hashKey, queryExpression.getIndexName()));
                return new PaginatedQueryList<T>(
                        this, clazz, null, new QueryRequest(), new QueryResult().withItems(emptyList()),
                        DynamoDBMapperConfig.PaginationLoadingStrategy.EAGER_LOADING, null) {
                    public T get(int n) {
                        return results.get(n);

                    public boolean isEmpty() {
                        return results.isEmpty();

                    public Iterator<T> iterator() {
                        return results.iterator();

            private <T> List findFields(Class<T> clazz, String indexName) {
                return stream(clazz.getDeclaredFields()).filter(f ->
                        (indexName == null && f.getAnnotationsByType(DynamoDBHashKey.class).length > 0
                                || indexName != null &&
                                ((f.getAnnotationsByType(DynamoDBAttribute.class).length > 0 &&
                                        Objects.equals(f.getAnnotationsByType(DynamoDBAttribute.class)[0].attributeName(), indexName)) ||
                                        (f.getAnnotationsByType(DynamoDBIndexHashKey.class).length > 0 &&
                                                Objects.equals(f.getAnnotationsByType(DynamoDBIndexHashKey.class)[0].globalSecondaryIndexName(), indexName)))))

            private <T> Object findHashKey(T candidate, DynamoDBQueryExpression<T> queryExpression) {
                return findFields(candidate.getClass(), queryExpression == null ? null : queryExpression.getIndexName()).stream()
                        .findFirst().map(f -> {
                            try {
                                return f.get(candidate);
                            } catch (IllegalAccessException e) {
                                return null;

            public <T> T load(Class<T> clazz, Object hashKey) {
                return findElement(clazz, hashKey, null);

            public <T> T findElement(Class<T> clazz, Object hashKey, String indexName) {
                Set<T> set = (Set<T>) tables.computeIfAbsent(clazz.getName(), k -> new HashSet());
                List keyFields = findFields(clazz, indexName);

                return set.stream().filter(element -> keyFields.stream().anyMatch(f -> {
                    try {
                        return Objects.equals(f.get(element), hashKey);
                    } catch (IllegalAccessException e) {
                        return false;

Now make it work… how ? just keep it simple.

Leave a Reply

Your email address will not be published. Required fields are marked *


This site uses Akismet to reduce spam. Learn how your comment data is processed.