Test framework library for Elasticsearch providing comprehensive testing utilities, base test classes, cluster management, and assertion helpers for unit and integration testing of Elasticsearch plugins and applications
—
The Elasticsearch test framework provides extensive assertion utilities and custom Hamcrest matchers for validating search results, cluster state, responses, and other Elasticsearch-specific data structures. These utilities enable expressive and maintainable test assertions.
The core assertion utility class providing high-level assertions for Elasticsearch operations and responses.
package org.elasticsearch.test.hamcrest;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.hamcrest.Matcher;
/**
* Static utility methods for creating Elasticsearch-specific assertions and matchers.
* Provides fluent, readable assertions for common Elasticsearch testing scenarios.
*/
public class ElasticsearchAssertions {
/**
* Asserts that an ActionFuture completes successfully within the default timeout.
*
* @param future the future to wait for
* @param <T> response type
* @return the response from the future
* @throws AssertionError if future fails or times out
*/
public static <T extends ActionResponse> T assertAcked(ActionFuture<T> future);
/**
* Asserts that a response indicates an acknowledged operation.
*
* @param response response to check
* @throws AssertionError if operation was not acknowledged
*/
public static void assertAcked(IsAcknowledgedSupplier response);
/**
* Asserts that a bulk response has no failures.
*
* @param response bulk response to check
* @throws AssertionError if any items in the bulk response failed
*/
public static void assertNoFailures(BulkResponse response);
/**
* Asserts that a search response completed successfully with no failures.
*
* @param response search response to check
* @throws AssertionError if search had shard failures or timed out
*/
public static void assertNoFailures(SearchResponse response);
/**
* Asserts that a cluster health response indicates the expected health status.
*
* @param response cluster health response
* @param expectedStatus expected health status
* @throws AssertionError if health status doesn't match
*/
public static void assertHealthStatus(ClusterHealthResponse response,
ClusterHealthStatus expectedStatus);
/**
* Asserts that a search response contains the expected number of hits.
*
* @param response search response to check
* @param expectedHits expected total hit count
* @throws AssertionError if hit count doesn't match
*/
public static void assertHitCount(SearchResponse response, long expectedHits);
/**
* Asserts that a search response contains exactly one hit.
*
* @param response search response to check
* @throws AssertionError if hit count is not exactly 1
*/
public static void assertSingleHit(SearchResponse response);
/**
* Asserts that a search response contains no hits.
*
* @param response search response to check
* @throws AssertionError if any hits are found
*/
public static void assertNoSearchHits(SearchResponse response);
/**
* Asserts that search hits are ordered according to the specified sort criteria.
*
* @param response search response to check
* @param sortField field used for sorting
* @param ascending true if sort should be ascending, false for descending
* @throws AssertionError if hits are not properly sorted
*/
public static void assertOrderedSearchHits(SearchResponse response,
String sortField,
boolean ascending);
/**
* Asserts that a SearchHit contains a field with the expected value.
*
* @param hit search hit to check
* @param field field name to check
* @param expectedValue expected field value
* @throws AssertionError if field value doesn't match
*/
public static void assertSearchHitHasField(SearchHit hit, String field, Object expectedValue);
/**
* Asserts that a GetResponse indicates a document exists.
*
* @param response get response to check
* @throws AssertionError if document doesn't exist
*/
public static void assertDocumentExists(GetResponse response);
/**
* Asserts that a GetResponse indicates a document does not exist.
*
* @param response get response to check
* @throws AssertionError if document exists
*/
public static void assertDocumentMissing(GetResponse response);
/**
* Waits for and asserts cluster health reaches the specified status.
*
* @param client client to use for health checks
* @param status expected health status
* @param indices indices to check (empty for all indices)
*/
public static void ensureHealth(Client client,
ClusterHealthStatus status,
String... indices);
/**
* Waits for cluster to reach green health status.
*
* @param client client to use for health checks
* @param indices indices to check (empty for all indices)
*/
public static void ensureGreen(Client client, String... indices);
/**
* Waits for cluster to reach yellow or green health status.
*
* @param client client to use for health checks
* @param indices indices to check (empty for all indices)
*/
public static void ensureYellow(Client client, String... indices);
/**
* Asserts that two BytesReference objects are equal.
*
* @param expected expected bytes
* @param actual actual bytes
* @throws AssertionError if bytes don't match
*/
public static void assertBytesEqual(BytesReference expected, BytesReference actual);
/**
* Asserts that an exception was caused by a specific root cause.
*
* @param exception exception to examine
* @param expectedCause expected root cause class
* @throws AssertionError if root cause doesn't match
*/
public static void assertCausedBy(Throwable exception, Class<? extends Throwable> expectedCause);
}package org.elasticsearch.test.hamcrest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.hamcrest.Matcher;
/**
* Custom Hamcrest matchers for SearchResponse validation.
*/
public class SearchMatchers {
/**
* Matches SearchResponse with expected hit count.
*
* @param expectedCount expected number of hits
* @return matcher for hit count
*/
public static Matcher<SearchResponse> hasHitCount(long expectedCount);
/**
* Matches SearchResponse with hit count in specified range.
*
* @param min minimum hit count (inclusive)
* @param max maximum hit count (inclusive)
* @return matcher for hit count range
*/
public static Matcher<SearchResponse> hasHitCount(long min, long max);
/**
* Matches SearchResponse with no search failures.
*
* @return matcher for successful search
*/
public static Matcher<SearchResponse> isSuccessfulSearchResponse();
/**
* Matches SearchResponse that completed without timeout.
*
* @return matcher for non-timed-out search
*/
public static Matcher<SearchResponse> searchNotTimedOut();
/**
* Matches SearchResponse containing a hit with specified ID.
*
* @param id document ID to match
* @return matcher for document ID
*/
public static Matcher<SearchResponse> hasHitWithId(String id);
/**
* Matches SearchResponse where hits are ordered by specified field.
*
* @param field field name used for ordering
* @param ascending true for ascending order, false for descending
* @return matcher for search hit ordering
*/
public static Matcher<SearchResponse> orderedBy(String field, boolean ascending);
/**
* Matches SearchHit containing a field with the expected value.
*
* @param fieldName field name to check
* @param expectedValue expected field value
* @return matcher for search hit field
*/
public static Matcher<SearchHit> hasField(String fieldName, Object expectedValue);
/**
* Matches SearchHit with expected document ID.
*
* @param expectedId expected document ID
* @return matcher for document ID
*/
public static Matcher<SearchHit> hasId(String expectedId);
/**
* Matches SearchHit with expected document type.
*
* @param expectedType expected document type
* @return matcher for document type
*/
public static Matcher<SearchHit> hasType(String expectedType);
/**
* Matches SearchHit with expected index name.
*
* @param expectedIndex expected index name
* @return matcher for index name
*/
public static Matcher<SearchHit> hasIndex(String expectedIndex);
/**
* Matches SearchHit with score greater than or equal to minimum.
*
* @param minScore minimum score threshold
* @return matcher for minimum score
*/
public static Matcher<SearchHit> hasScore(float minScore);
/**
* Matches SearchHit containing highlighted fields.
*
* @param fieldName field that should be highlighted
* @return matcher for highlighted field
*/
public static Matcher<SearchHit> hasHighlighting(String fieldName);
}package org.elasticsearch.test.hamcrest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.hamcrest.Matcher;
/**
* Hamcrest matchers for cluster health assertions.
*/
public class ClusterHealthMatchers {
/**
* Matches ClusterHealthResponse with expected status.
*
* @param expectedStatus expected cluster health status
* @return matcher for health status
*/
public static Matcher<ClusterHealthResponse> hasStatus(ClusterHealthStatus expectedStatus);
/**
* Matches ClusterHealthResponse indicating cluster is not timed out.
*
* @return matcher for non-timed-out cluster
*/
public static Matcher<ClusterHealthResponse> isNotTimedOut();
/**
* Matches ClusterHealthResponse with expected number of nodes.
*
* @param expectedNodes expected node count
* @return matcher for node count
*/
public static Matcher<ClusterHealthResponse> hasNodes(int expectedNodes);
/**
* Matches ClusterHealthResponse with expected number of data nodes.
*
* @param expectedDataNodes expected data node count
* @return matcher for data node count
*/
public static Matcher<ClusterHealthResponse> hasDataNodes(int expectedDataNodes);
/**
* Matches ClusterHealthResponse with no relocating shards.
*
* @return matcher for no relocating shards
*/
public static Matcher<ClusterHealthResponse> hasNoRelocatingShards();
/**
* Matches ClusterHealthResponse with no initializing shards.
*
* @return matcher for no initializing shards
*/
public static Matcher<ClusterHealthResponse> hasNoInitializingShards();
/**
* Matches ClusterHealthResponse with no unassigned shards.
*
* @return matcher for no unassigned shards
*/
public static Matcher<ClusterHealthResponse> hasNoUnassignedShards();
}package org.elasticsearch.test.hamcrest;
import java.util.Collection;
import java.util.Optional;
import org.hamcrest.Matcher;
/**
* Hamcrest matchers for Optional and Collection types.
*/
public class OptionalMatchers {
/**
* Matches Optional that is present.
*
* @param <T> type of Optional content
* @return matcher for present Optional
*/
public static <T> Matcher<Optional<T>> isPresent();
/**
* Matches Optional that is present and contains the expected value.
*
* @param expectedValue expected Optional content
* @param <T> type of Optional content
* @return matcher for Optional with specific value
*/
public static <T> Matcher<Optional<T>> isPresentWith(T expectedValue);
/**
* Matches Optional that is empty.
*
* @param <T> type of Optional content
* @return matcher for empty Optional
*/
public static <T> Matcher<Optional<T>> isEmpty();
/**
* Matches Collection containing all specified items.
*
* @param items items that should be present
* @param <T> type of Collection elements
* @return matcher for Collection containing items
*/
public static <T> Matcher<Collection<T>> containsAll(T... items);
/**
* Matches Collection with expected size.
*
* @param expectedSize expected collection size
* @param <T> type of Collection elements
* @return matcher for Collection size
*/
public static <T> Matcher<Collection<T>> hasSize(int expectedSize);
/**
* Matches empty Collection.
*
* @param <T> type of Collection elements
* @return matcher for empty Collection
*/
public static <T> Matcher<Collection<T>> emptyCollection();
}package org.elasticsearch.test.hamcrest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.common.collect.Tuple;
import org.hamcrest.Matcher;
/**
* Specialized matchers for geometric and tuple data structures.
*/
public class TupleMatchers {
/**
* Matches Tuple with expected first element.
*
* @param expectedFirst expected first tuple element
* @param <T1> type of first element
* @param <T2> type of second element
* @return matcher for first tuple element
*/
public static <T1, T2> Matcher<Tuple<T1, T2>> hasFirst(T1 expectedFirst);
/**
* Matches Tuple with expected second element.
*
* @param expectedSecond expected second tuple element
* @param <T1> type of first element
* @param <T2> type of second element
* @return matcher for second tuple element
*/
public static <T1, T2> Matcher<Tuple<T1, T2>> hasSecond(T2 expectedSecond);
/**
* Matches Tuple with both expected elements.
*
* @param expectedFirst expected first element
* @param expectedSecond expected second element
* @param <T1> type of first element
* @param <T2> type of second element
* @return matcher for complete tuple
*/
public static <T1, T2> Matcher<Tuple<T1, T2>> isTuple(T1 expectedFirst, T2 expectedSecond);
}
/**
* Matchers for geometric Rectangle objects.
*/
public class RectangleMatchers {
/**
* Matches Rectangle containing the specified point.
*
* @param point point that should be contained
* @return matcher for point containment
*/
public static Matcher<Rectangle> containsPoint(GeoPoint point);
/**
* Matches Rectangle with expected bounds.
*
* @param minLon minimum longitude
* @param maxLon maximum longitude
* @param minLat minimum latitude
* @param maxLat maximum latitude
* @return matcher for rectangle bounds
*/
public static Matcher<Rectangle> hasBounds(double minLon, double maxLon,
double minLat, double maxLat);
/**
* Matches Rectangle that intersects with another rectangle.
*
* @param other rectangle to check intersection with
* @return matcher for rectangle intersection
*/
public static Matcher<Rectangle> intersects(Rectangle other);
}import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.elasticsearch.test.hamcrest.SearchMatchers.*;
public class SearchAssertionTest extends ESIntegTestCase {
public void testSearchAssertions() {
createIndex("test-index");
// Index test documents
client().prepareIndex("test-index")
.setId("1")
.setSource("title", "First Document", "score", 85)
.get();
client().prepareIndex("test-index")
.setId("2")
.setSource("title", "Second Document", "score", 92)
.get();
refresh("test-index");
// Search and use assertions
SearchResponse response = client().prepareSearch("test-index")
.setQuery(QueryBuilders.matchAllQuery())
.addSort("score", SortOrder.DESC)
.get();
// Basic assertions
assertNoFailures(response);
assertHitCount(response, 2);
// Hamcrest matchers
assertThat(response, hasHitCount(2));
assertThat(response, isSuccessfulSearchResponse());
assertThat(response, hasHitWithId("1"));
assertThat(response, orderedBy("score", false)); // descending
// Individual hit assertions
SearchHit firstHit = response.getHits().getHits()[0];
assertThat(firstHit, hasId("2")); // Higher score document first
assertThat(firstHit, hasField("title", "Second Document"));
assertThat(firstHit, hasScore(0.0f)); // Match all query has score
// Combined assertions
assertSingleHit(client().prepareSearch("test-index")
.setQuery(QueryBuilders.termQuery("title", "first"))
.get());
}
public void testClusterHealthAssertions() {
createIndex("health-test");
// Wait for and assert cluster health
ensureGreen("health-test");
// Manual health check with assertions
ClusterHealthResponse health = client().admin().cluster()
.prepareHealth("health-test")
.get();
assertHealthStatus(health, ClusterHealthStatus.GREEN);
assertThat(health, hasStatus(ClusterHealthStatus.GREEN));
assertThat(health, isNotTimedOut());
assertThat(health, hasNoUnassignedShards());
assertThat(health, hasDataNodes(numDataNodes()));
}
public void testBulkOperationAssertions() {
createIndex("bulk-test");
BulkRequestBuilder bulk = client().prepareBulk();
for (int i = 0; i < 10; i++) {
bulk.add(client().prepareIndex("bulk-test")
.setId(String.valueOf(i))
.setSource("field", "value" + i));
}
BulkResponse bulkResponse = bulk.get();
// Assert no bulk failures
assertNoFailures(bulkResponse);
assertThat("All items should succeed",
bulkResponse.hasFailures(), equalTo(false));
assertThat("Should have 10 items",
bulkResponse.getItems().length, equalTo(10));
}
}import static org.elasticsearch.test.hamcrest.OptionalMatchers.*;
import static org.elasticsearch.test.hamcrest.TupleMatchers.*;
public class CustomMatcherTest extends ESTestCase {
public void testOptionalMatchers() {
Optional<String> presentValue = Optional.of("test");
Optional<String> emptyValue = Optional.empty();
assertThat(presentValue, isPresent());
assertThat(presentValue, isPresentWith("test"));
assertThat(emptyValue, isEmpty());
}
public void testTupleMatchers() {
Tuple<String, Integer> tuple = new Tuple<>("hello", 42);
assertThat(tuple, hasFirst("hello"));
assertThat(tuple, hasSecond(42));
assertThat(tuple, isTuple("hello", 42));
}
public void testCollectionMatchers() {
List<String> items = Arrays.asList("a", "b", "c");
assertThat(items, hasSize(3));
assertThat(items, containsAll("a", "c"));
assertThat(Collections.emptyList(), emptyCollection());
}
}public class ExceptionAssertionTest extends ESTestCase {
public void testExceptionAssertions() {
// Test expected exceptions with root cause checking
ElasticsearchException ex = expectThrows(ElasticsearchException.class, () -> {
throw new ElasticsearchException("Test error",
new IllegalArgumentException("Root cause"));
});
assertCausedBy(ex, IllegalArgumentException.class);
assertThat(ex.getMessage(), containsString("Test error"));
}
public void testSearchFailureAssertions() {
createIndex("test-index");
// Test search with invalid query
SearchPhaseExecutionException ex = expectThrows(
SearchPhaseExecutionException.class,
() -> client().prepareSearch("test-index")
.setQuery(QueryBuilders.scriptQuery(
new Script("invalid script that will fail")))
.get()
);
assertThat("Should have shard failures",
ex.shardFailures().length, greaterThan(0));
}
}ElasticsearchAssertions static methods for common scenariosallOf() and anyOf() for complex conditionsThe assertion and matcher utilities provide a rich vocabulary for expressing test expectations clearly and maintaining readable, reliable test code.
Install with Tessl CLI
npx tessl i tessl/maven-org-elasticsearch--elasticsearch-test-framework