src/main/java/net/ttddyy/dsproxy/asserts/ProxyTestDataSource.java
public class ProxyTestDataSource extends ProxyDataSource {

    private QueryExecutionFactoryListener queryExecutionFactoryListener = new QueryExecutionFactoryListener();


    public ProxyTestDataSource() {
        initialize();
    }

    public ProxyTestDataSource(DataSource dataSource) {
        super(dataSource);
        initialize();
    }

    private void initialize() {
        this.getProxyConfig().getQueryListener().addListener(this.queryExecutionFactoryListener);
    }

    public void reset() {
        this.queryExecutionFactoryListener.reset();
    }

    public List<StatementExecution> getStatements() {
        return getQueryExecutionsFilteredBy(StatementExecution.class);
    }

    public StatementExecution getFirstStatement() {
        return getFirstQueryExecution(StatementExecution.class);
    }

    public StatementExecution getLastStatement() {
        return getLastQueryExecution(StatementExecution.class);
    }

    public List<StatementBatchExecution> getBatchStatements() {
        return getQueryExecutionsFilteredBy(StatementBatchExecution.class);
    }

    public StatementBatchExecution getFirstBatchStatement() {
        return getFirstQueryExecution(StatementBatchExecution.class);
    }

    public StatementBatchExecution getLastBatchStatement() {
        return getLastQueryExecution(StatementBatchExecution.class);
    }

    public List<PreparedExecution> getPrepareds() {
        return getQueryExecutionsFilteredBy(PreparedExecution.class);
    }

    public PreparedExecution getFirstPrepared() {
        return getFirstQueryExecution(PreparedExecution.class);
    }

    public PreparedExecution getLastPrepared() {
        return getLastQueryExecution(PreparedExecution.class);
    }

    public List<PreparedBatchExecution> getBatchPrepareds() {
        return getQueryExecutionsFilteredBy(PreparedBatchExecution.class);
    }

    public PreparedBatchExecution getFirstBatchPrepared() {
        return getFirstQueryExecution(PreparedBatchExecution.class);
    }

    public PreparedBatchExecution getLastBatchPrepared() {
        return getLastQueryExecution(PreparedBatchExecution.class);
    }

    public List<CallableExecution> getCallables() {
        return getQueryExecutionsFilteredBy(CallableExecution.class);
    }

    public CallableExecution getFirstCallable() {
        return getFirstQueryExecution(CallableExecution.class);
    }

    public CallableExecution getLastCallable() {
        return getLastQueryExecution(CallableExecution.class);
    }

    public List<CallableBatchExecution> getBatchCallables() {
        return getQueryExecutionsFilteredBy(CallableBatchExecution.class);
    }

    public CallableBatchExecution getFirstBatchCallable() {
        return getFirstQueryExecution(CallableBatchExecution.class);
    }

    public CallableBatchExecution getLastBatchCallable() {
        return getLastQueryExecution(CallableBatchExecution.class);
    }


    @SuppressWarnings("unchecked")
    private <T extends QueryExecution> List<T> getQueryExecutionsFilteredBy(Class<T> classToFilter) {
        List<T> result = new ArrayList<>();
        for (QueryExecution queryExecution : getQueryExecutions()) {
            if (classToFilter.isAssignableFrom(queryExecution.getClass())) {
                result.add((T) queryExecution);
            }
        }
        return result;
    }

    private <T extends QueryExecution> T getFirstQueryExecution(Class<T> classToFilter) {
        List<T> filtered = getQueryExecutionsFilteredBy(classToFilter);
        if (filtered.isEmpty()) {
            return null;
        }
        return filtered.get(0);
    }

    private <T extends QueryExecution> T getLastQueryExecution(Class<T> classToFilter) {
        List<T> filtered = getQueryExecutionsFilteredBy(classToFilter);
        if (filtered.isEmpty()) {
            return null;
        }
        return filtered.get(filtered.size() - 1);
    }

    public List<QueryExecution> getQueryExecutions() {
        return this.queryExecutionFactoryListener.getQueryExecutions();
    }

    public QueryExecutionFactoryListener getQueryExecutionFactoryListener() {
        return this.queryExecutionFactoryListener;
    }
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> selectCount(int count) {
    return new QueryTypeCountMatcher(QueryType.SELECT, count);
}
src/main/java/net/ttddyy/dsproxy/asserts/PreparedBatchExecution.java
public class PreparedBatchExecution extends BaseQueryExecution implements QueryHolder, BatchParameterHolder, BatchExecution {

    private String query;
    private List<BatchExecutionEntry> batchExecutionEntries = new ArrayList<>();

    @Override
    public boolean isBatch() {
        return true;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    @Override
    public List<BatchExecutionEntry> getBatchExecutionEntries() {
        return this.batchExecutionEntries;
    }

    public boolean addBatchExecutionEntry(PreparedBatchExecutionEntry entry) {
        return this.batchExecutionEntries.add(entry);
    }

    @SuppressWarnings("unchecked")
    public List<PreparedBatchExecutionEntry> getPrepareds() {

        return (List<PreparedBatchExecutionEntry>) (List<?>) this.batchExecutionEntries;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> preparedOrBatchPreparedCount(int count) {
    String msg = "PreparedExecution or PreparedBatchExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getPrepareds().size() + actual.getBatchPrepareds().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/QueryExecution.java
public interface QueryExecution {

    boolean isSuccess();

    boolean isBatch();

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> insertCount(int count) {
    return new QueryTypeCountMatcher(QueryType.INSERT, count);
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> executions(final int index,
        Matcher<? super QueryExecution> queryExecutionMatcher) {
    return new CompositeMatcher<ProxyTestDataSource, QueryExecution>(queryExecutionMatcher) {

        @Override
        protected boolean validateByThisMatcher(ProxyTestDataSource item, Description expected,
                Description actual) {
            List<QueryExecution> queryExecutions = item.getQueryExecutions();
            int size = queryExecutions.size();
            if (size - 1 < index) {
                expected.appendText("queryExecutions[" + index + "] exists");
                actual.appendText("queryExecutions[] size was " + size);
                return false;
            }
            return true;
        }

        @Override
        public QueryExecution getValue(ProxyTestDataSource actual) {
            return actual.getQueryExecutions().get(index);
        }

        @Override
        public String getSubMatcherFailureDescriptionPrefix() {
            return "queryExecutions[" + index + "] ";
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> preparedCount(int count) {
    String msg = "PreparedExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getPrepareds().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/QueryExecutionAssertions.java
public static Matcher<QueryExecution> success() {
    return new TypeSafeMatcher<QueryExecution>() {
        @Override
        protected boolean matchesSafely(QueryExecution item) {
            return item.isSuccess();
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("success");
        }

        @Override
        protected void describeMismatchSafely(QueryExecution item, Description mismatchDescription) {
            mismatchDescription.appendText("was failure");
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/CallableExecution.java
public class CallableExecution extends BaseQueryExecution implements QueryHolder, ParameterByIndexHolder, ParameterByNameHolder, OutParameterHolder {

    private String query;
    private SortedSet<ParameterKeyValue> parameters = new TreeSet<>();


    @Override
    public boolean isBatch() {
        return false;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public SortedSet<ParameterKeyValue> getAllParameters() {
        return this.parameters;
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_PARAM);
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetNullParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_NULL);
    }

    @Override
    public SortedSet<ParameterKeyValue> getOutParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.REGISTER_OUT);
    }

    @Override
    public Map<String, Object> getSetParamsByName() {
        return toKeyNameMap(filterByKeyType(getSetParams(), ParameterKey.ParameterKeyType.BY_NAME));
    }

    @Override
    public Map<Integer, Object> getSetParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public Map<String, Integer> getSetNullParamsByName() {
        return toKeyNameMap(filterByKeyType(getSetNullParams(), ParameterKey.ParameterKeyType.BY_NAME));
    }

    @Override
    public Map<Integer, Integer> getSetNullParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetNullParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public Map<String, Object> getOutParamsByName() {
        return toKeyNameMap(filterByKeyType(getOutParams(), ParameterKey.ParameterKeyType.BY_NAME));
    }

    @Override
    public Map<Integer, Object> getOutParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getOutParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public List<String> getParamNames() {
        List<String> names = new ArrayList<>();
        names.addAll(getSetParamsByName().keySet());
        names.addAll(getSetNullParamsByName().keySet());
        return names;
    }

    @Override
    public List<Integer> getParamIndexes() {
        List<Integer> indexes = new ArrayList<>();
        indexes.addAll(getSetParamsByIndex().keySet());
        indexes.addAll(getSetNullParamsByIndex().keySet());
        return indexes;
    }

    @Override
    public List<String> getOutParamNames() {
        return new ArrayList<>(getOutParamsByName().keySet());
    }

    @Override
    public List<Integer> getOutParamIndexes() {
        return new ArrayList<>(getOutParamsByIndex().keySet());
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> callableOrBatchCallableCount(int count) {
    String msg = "CallableExecution or CallableBatchExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getCallables().size() + actual.getBatchCallables().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/QueryHolder.java
public interface QueryHolder {

    String getQuery();

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> batchStatementCount(int count) {
    String msg = "StatementBatchExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getBatchStatements().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/StatementExecution.java
public class StatementExecution extends BaseQueryExecution implements QueryHolder {

    private String query;

    @Override
    public boolean isBatch() {
        return false;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/QueryExecutionAssertions.java
public static Matcher<QueryExecution> failure() {
    return new TypeSafeMatcher<QueryExecution>() {
        @Override
        protected boolean matchesSafely(QueryExecution item) {
            return !item.isSuccess();
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("failure");
        }

        @Override
        protected void describeMismatchSafely(QueryExecution item, Description mismatchDescription) {
            mismatchDescription.appendText("was success");
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> callableCount(int count) {
    String msg = "CallableExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getCallables().size();
        }
    };

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> executionCount(int count) {
    String msg = "queryExecutions size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getQueryExecutions().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> deleteCount(int count) {
    return new QueryTypeCountMatcher(QueryType.DELETE, count);
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> updateCount(int count) {
    return new QueryTypeCountMatcher(QueryType.UPDATE, count);
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> batchPreparedCount(int count) {
    String msg = "PreparedBatchExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getBatchPrepareds().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> statementOrBatchStatementCount(int count) {
    String msg = "StatementExecution or StatementBatchExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getStatements().size() + actual.getBatchStatements().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/StatementBatchExecution.java
public class StatementBatchExecution extends BaseQueryExecution implements QueriesHolder, BatchExecution {

    private List<String> queries = new ArrayList<>();

    @Override
    public boolean isBatch() {
        return true;
    }

    @Override
    public List<String> getQueries() {
        return this.queries;
    }

    public void setQueries(List<String> queries) {
        this.queries = queries;
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> otherCount(int count) {
    return new QueryTypeCountMatcher(QueryType.OTHER, count);
}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> batchCallableCount(int count) {
    String msg = "CallableBatchExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getBatchCallables().size();
        }
    };

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> statementCount(int count) {
    String msg = "StatementExecution size";
    return new FeatureMatcher<ProxyTestDataSource, Integer>(equalTo(count), msg, msg) {
        @Override
        protected Integer featureValueOf(ProxyTestDataSource actual) {
            return actual.getStatements().size();
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/CallableBatchExecution.java
public class CallableBatchExecution extends BaseQueryExecution implements BatchParameterHolder, QueryHolder, BatchExecution {

    private String query;

    private List<BatchExecutionEntry> batchExecutionEntries = new ArrayList<>();

    @Override
    public boolean isBatch() {
        return true;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    @Override
    public List<BatchExecutionEntry> getBatchExecutionEntries() {
        return this.batchExecutionEntries;
    }

    public boolean addBatchExecutionEntry(CallableBatchExecutionEntry entry) {
        return this.batchExecutionEntries.add(entry);
    }

}
src/main/java/net/ttddyy/dsproxy/asserts/hamcrest/ProxyTestDataSourceAssertions.java
public static Matcher<ProxyTestDataSource> totalQueryCount(final int count) {
    return new TypeSafeMatcher<ProxyTestDataSource>() {
        @Override
        protected boolean matchesSafely(ProxyTestDataSource item) {
            int actualSize = countQueries(item);
            return count == actualSize;
        }

        @Override
        public void describeTo(Description description) {
            // expected clause
            description.appendText(count + " query executions");
        }

        @Override
        protected void describeMismatchSafely(ProxyTestDataSource item,
                Description mismatchDescription) {
            // but was clause
            int actualSize = countQueries(item);
            mismatchDescription.appendText("was " + actualSize + " query executions");
        }

        private int countQueries(ProxyTestDataSource ds) {
            int count = 0;
            for (QueryExecution queryExecution : ds.getQueryExecutions()) {
                if (queryExecution instanceof QueryHolder) {
                    count++;
                } else if (queryExecution instanceof QueriesHolder) {
                    count += ((QueriesHolder) queryExecution).getQueries().size();
                }
            }
            return count;
        }
    };
}
src/main/java/net/ttddyy/dsproxy/asserts/PreparedExecution.java
public class PreparedExecution extends BaseQueryExecution implements QueryHolder, ParameterByIndexHolder {

    private String query;
    private SortedSet<ParameterKeyValue> parameters = new TreeSet<>();

    @Override
    public boolean isBatch() {
        return false;
    }

    @Override
    public String getQuery() {
        return this.query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public SortedSet<ParameterKeyValue> getAllParameters() {
        return this.parameters;
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_PARAM);
    }

    @Override
    public SortedSet<ParameterKeyValue> getSetNullParams() {
        return filterBy(this.parameters, ParameterKeyValue.OperationType.SET_NULL);
    }

    @Override
    public Map<Integer, Object> getSetParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public Map<Integer, Integer> getSetNullParamsByIndex() {
        return toKeyIndexMap(filterByKeyType(getSetNullParams(), ParameterKey.ParameterKeyType.BY_INDEX));
    }

    @Override
    public List<Integer> getParamIndexes() {
        List<Integer> indexes = new ArrayList<>();
        indexes.addAll(getSetParamsByIndex().keySet());
        indexes.addAll(getSetNullParamsByIndex().keySet());
        return indexes;
    }

}


Question: package net.ttddyy.dsproxy.asserts.hamcrest;
import net.ttddyy.dsproxy.asserts.CallableBatchExecution;
import net.ttddyy.dsproxy.asserts.CallableExecution;
import net.ttddyy.dsproxy.asserts.PreparedBatchExecution;
import net.ttddyy.dsproxy.asserts.PreparedExecution;
import net.ttddyy.dsproxy.asserts.ProxyTestDataSource;
import net.ttddyy.dsproxy.asserts.QueryExecution;
import net.ttddyy.dsproxy.asserts.QueryHolder;
import net.ttddyy.dsproxy.asserts.StatementBatchExecution;
import net.ttddyy.dsproxy.asserts.StatementExecution;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.batchCallableCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.batchPreparedCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.batchStatementCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.callableCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.callableOrBatchCallableCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.deleteCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.executionCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.executions;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.insertCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.otherCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.preparedCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.preparedOrBatchPreparedCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.selectCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.statementCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.statementOrBatchStatementCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.totalQueryCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.ProxyTestDataSourceAssertions.updateCount;
import static net.ttddyy.dsproxy.asserts.hamcrest.QueryExecutionAssertions.failure;
import static net.ttddyy.dsproxy.asserts.hamcrest.QueryExecutionAssertions.success;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;





/**
 * @author Tadaya Tsuyukubo
 * @since 1.0
 */
public class ProxyTestDataSourceAssertionsTest {


    @Test
    public void testExecutionSuccess() {
        StatementExecution se = new StatementExecution();
        se.setSuccess(true);

        ProxyTestDataSource ds = new ProxyTestDataSource();
        ds.getQueryExecutions().add(se);

        Assert.assertThat(ds, executions(0, success()));
        Assert.assertThat(ds, executions(0, is(success())));
    }

    @Test
    public void testExecutionFail() {
        StatementExecution se = new StatementExecution();
        se.setSuccess(false);

        ProxyTestDataSource ds = new ProxyTestDataSource();
        ds.getQueryExecutions().add(se);

        Assert.assertThat(ds, executions(0, failure()));
        Assert.assertThat(ds, executions(0, is(failure())));
    }

    @Test
    public void executionsAssertionMessage() {
        StatementExecution se = new StatementExecution();
        se.setSuccess(false);

        ProxyTestDataSource ds = new ProxyTestDataSource();
        ds.getQueryExecutions().add(se);

        try {
            Assert.assertThat(ds, executions(0, success()));
            fail("AssertionError should be thrown");
        } catch (AssertionError e) {
            assertThat(e).hasMessage("\nExpected: queryExecutions[0] success\n     but: queryExecutions[0] was failure");
        }
    }

    @Test
    public void executionsAssertionMessageForOutOfIndex() {
        StatementExecution se = new StatementExecution();
        se.setSuccess(false);

        ProxyTestDataSource ds = new ProxyTestDataSource();
        ds.getQueryExecutions().add(se);

        try {
            Assert.assertThat(ds, executions(10, success()));
            fail("AssertionError should be thrown");
        } catch (AssertionError e) {
            assertThat(e).hasMessage("\nExpected: queryExecutions[10] exists\n     but: queryExecutions[] size was 1");
        }
    }


    @Test
    public void testTotalCount() {
        List<QueryExecution> queryExecutions = new ArrayList<>();
        queryExecutions.add(getMockSelectQueryExecution());  // select
        queryExecutions.add(getMockSelectQueryExecution());  // select
        queryExecutions.add(getMockInsertQueryExecution());  // insert
        queryExecutions.add(getMockUpdateQueryExecution());  // update
        queryExecutions.add(getMockDeleteQueryExecution());  // delete

        ProxyTestDataSource ds = mock(ProxyTestDataSource.class);
        given(ds.getQueryExecutions()).willReturn(queryExecutions);

Answer:         Assert.assertThat(ds, totalQueryCount(5));