CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-elasticsearch--elasticsearch-test-framework

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

Pending
Overview
Eval results
Files

core-testing.mddocs/

Core Testing Classes

The core testing classes in org.elasticsearch.test provide the foundation for all Elasticsearch testing. These base classes extend Apache Lucene's testing infrastructure with Elasticsearch-specific functionality, random data generation, and cluster management capabilities.

ESTestCase

The primary base class for all Elasticsearch unit tests, extending Lucene's LuceneTestCase with Elasticsearch-specific utilities.

package org.elasticsearch.test;

import com.carrotsearch.randomizedtesting.RandomizedTest;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.core.TimeValue;

/**
 * Base class for all Elasticsearch unit tests providing randomized testing utilities,
 * proper setup/teardown lifecycle, and Elasticsearch-specific test infrastructure.
 */
@ThreadLeakScope(Scope.NONE)
@ThreadLeakFilters(filters = {ESTestCase.ElasticsearchThreadFilter.class})
@TimeoutSuite(millis = TimeUnits.HOUR)
@LuceneTestCase.SuppressReproduceLine
public abstract class ESTestCase extends LuceneTestCase {
    
    /**
     * Sets up test environment before each test method.
     * Initializes logging, thread context, and randomization seeds.
     */
    @Before
    public void setUp() throws Exception;
    
    /**
     * Cleans up test environment after each test method.
     * Ensures proper resource cleanup and thread termination.
     */
    @After  
    public void tearDown() throws Exception;
    
    /**
     * Generates a random string of the specified length using alphanumeric characters.
     * 
     * @param length the length of the string to generate
     * @return a random alphanumeric string
     */
    public static String randomAlphaOfLength(int length);
    
    /**
     * Generates a random string between the specified minimum and maximum length.
     * 
     * @param minLength minimum length (inclusive)
     * @param maxLength maximum length (inclusive) 
     * @return a random alphanumeric string
     */
    public static String randomAlphaOfLengthBetween(int minLength, int maxLength);
    
    /**
     * Returns a random integer between min and max (both inclusive).
     * 
     * @param min minimum value (inclusive)
     * @param max maximum value (inclusive)
     * @return random integer in the specified range
     */
    public static int randomIntBetween(int min, int max);
    
    /**
     * Returns a random integer scaled by the test multiplier for performance testing.
     * 
     * @param min minimum value (inclusive)
     * @param max maximum value (inclusive) 
     * @return scaled random integer
     */
    public static int scaledRandomIntBetween(int min, int max);
    
    /**
     * Picks a random element from the provided array.
     * 
     * @param array array to pick from
     * @param <T> type of array elements
     * @return randomly selected element
     */
    public static <T> T randomFrom(T... array);
    
    /**
     * Picks a random element from the provided list.
     * 
     * @param list list to pick from
     * @param <T> type of list elements
     * @return randomly selected element
     */
    public static <T> T randomFrom(List<T> list);
    
    /**
     * Returns a random boolean value.
     * 
     * @return true or false with equal probability
     */
    public static boolean randomBoolean();
    
    /**
     * Returns a random TimeValue for duration testing.
     * 
     * @return random TimeValue between 0 and 1 hour
     */
    public static TimeValue randomTimeValue();
    
    /**
     * Returns a random long value.
     * 
     * @return random long value
     */
    public static long randomLong();
    
    /**
     * Returns a random non-negative long value.
     * 
     * @return random non-negative long value
     */
    public static long randomNonNegativeLong();
    
    /**
     * Returns a random double value.
     * 
     * @return random double value
     */
    public static double randomDouble();
    
    /**
     * Returns a random float value.
     * 
     * @return random float value
     */
    public static float randomFloat();
    
    /**
     * Returns a random byte value.
     * 
     * @return random byte value
     */
    public static byte randomByte();
    
    /**
     * Returns a random UUID string.
     * 
     * @return random UUID string
     */
    public static String randomUUID();
    
    /**
     * Returns a random identifier string suitable for names/IDs.
     * 
     * @return random identifier string (8-12 lowercase chars)
     */
    public static String randomIdentifier();
    
    /**
     * Returns a random alphanumeric string of the specified length.
     * 
     * @param length the length of the string to generate
     * @return random alphanumeric string
     */
    public static String randomAlphanumericOfLength(int length);
    
    /**
     * Returns a random unicode string of the specified length.
     * 
     * @param codeUnits the length in code units
     * @return random unicode string
     */
    public static String randomUnicodeOfLength(int codeUnits);
    
    /**
     * Returns a random value other than the specified input.
     * 
     * @param input value to exclude
     * @param randomSupplier supplier for generating random values
     * @param <T> type of the value
     * @return random value different from input
     */
    public static <T> T randomValueOtherThan(T input, Supplier<T> randomSupplier);
    
    /**
     * Returns a random subset of the provided collection.
     * 
     * @param collection collection to select from
     * @param <T> type of collection elements
     * @return random subset as a list
     */
    public static <T> List<T> randomSubsetOf(Collection<T> collection);
    
    /**
     * Waits until the specified condition becomes true or timeout is reached.
     * 
     * @param breakSupplier condition to wait for
     * @param maxWaitTime maximum time to wait
     * @param unit time unit for maxWaitTime
     * @return true if condition was met, false if timeout
     */
    public static boolean waitUntil(BooleanSupplier breakSupplier, long maxWaitTime, TimeUnit unit);
    
    /**
     * Waits until the specified condition becomes true (10 second default timeout).
     * 
     * @param breakSupplier condition to wait for
     * @return true if condition was met, false if timeout
     */
    public static boolean waitUntil(BooleanSupplier breakSupplier);
}

ESTestCase Usage Examples

import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.common.settings.Settings;

public class MyUnitTest extends ESTestCase {
    
    public void testRandomDataGeneration() {
        // Generate random test data
        String randomName = randomAlphaOfLength(10);
        int randomPort = randomIntBetween(9200, 9300);
        boolean randomFlag = randomBoolean();
        
        // Test with random values
        MyService service = new MyService(randomName, randomPort, randomFlag);
        assertThat(service.getName(), equalTo(randomName));
        assertThat(service.getPort(), allOf(greaterThanOrEqualTo(9200), lessThanOrEqualTo(9300)));
    }
    
    public void testSettingsGeneration() {
        Settings settings = Settings.builder()
            .put("node.name", randomAlphaOfLength(8))
            .put("http.port", randomIntBetween(9200, 9299))
            .put("path.home", createTempDir())
            .build();
            
        assertThat(settings.get("node.name"), notNullValue());
        assertThat(settings.getAsInt("http.port", 0), greaterThan(9199));
    }
    
    public void testRandomValueGeneration() {
        // Generate various random values
        String uuid = randomUUID();
        String identifier = randomIdentifier();
        long randomLong = randomLong();
        double randomDouble = randomDouble();
        byte randomByte = randomByte();
        
        assertThat(uuid, notNullValue());
        assertThat(identifier.length(), allOf(greaterThanOrEqualTo(8), lessThanOrEqualTo(12)));
    }
    
    public void testAsyncOperation() {
        AtomicBoolean completed = new AtomicBoolean(false);
        
        // Start async operation
        myAsyncMethod(() -> completed.set(true));
        
        // Wait for completion (30 second timeout)
        boolean success = waitUntil(() -> completed.get(), 30, TimeUnit.SECONDS);
        
        assertTrue("Operation should complete", success && completed.get());
    }
}

ESIntegTestCase

Base class for integration tests that require a full Elasticsearch cluster. Manages cluster lifecycle and provides integration testing utilities.

package org.elasticsearch.test;

import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.common.settings.Settings;

/**
 * Base class for integration tests providing full Elasticsearch cluster management.
 * Supports single-node and multi-node cluster configurations with automatic lifecycle management.
 */
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1)
public abstract class ESIntegTestCase extends ESTestCase {
    
    /**
     * Defines the scope and configuration for test clusters.
     */
    public static @interface ClusterScope {
        Scope scope() default Scope.SUITE;
        int numDataNodes() default -1;
        int numClientNodes() default 0;
        boolean autoManageMasterNodes() default true;
        String[] supportedFeatures() default {};
    }
    
    /**
     * Enumeration of cluster scope options.
     */
    public enum Scope {
        /** Cluster shared across entire test suite */
        SUITE,
        /** New cluster for each test method */  
        TEST
    }
    
    /**
     * Returns the client for interacting with the test cluster.
     * 
     * @return client instance connected to the test cluster
     */
    public static Client client();
    
    /**
     * Returns a client connected to a specific node.
     * 
     * @param node node name or pattern
     * @return client for the specified node
     */
    public static Client client(String node);
    
    /**
     * Returns the internal test cluster for direct cluster management.
     * 
     * @return InternalTestCluster instance
     */  
    public static InternalTestCluster internalCluster();
    
    /**
     * Creates an index with the specified name and default settings.
     * 
     * @param indices index names to create
     */
    public static void createIndex(String... indices);
    
    /**
     * Creates an index with custom settings.
     * 
     * @param index index name
     * @param settings index settings
     */
    public static void createIndex(String index, Settings settings);
    
    /**
     * Creates an index using a builder for complex configuration.
     * 
     * @param name index name
     * @return CreateIndexRequestBuilder for fluent configuration
     */
    public static CreateIndexRequestBuilder prepareCreate(String name);
    
    /**
     * Waits for the cluster to reach green health status for the specified indices.
     * 
     * @param indices indices to wait for (empty means all indices)
     */
    public static void ensureGreen(String... indices);
    
    /**
     * Waits for the cluster to reach yellow or green health status.
     * 
     * @param indices indices to wait for (empty means all indices)
     */
    public static void ensureYellow(String... indices);
    
    /**
     * Waits for the cluster to have the specified health status.
     * 
     * @param status required health status
     * @param indices indices to check
     */
    public static void ensureHealth(ClusterHealthStatus status, String... indices);
    
    /**
     * Refreshes the specified indices to make documents searchable.
     * 
     * @param indices indices to refresh (empty means all indices)
     */
    public static void refresh(String... indices);
    
    /**
     * Flushes the specified indices to persist changes to disk.
     * 
     * @param indices indices to flush (empty means all indices)
     */  
    public static void flush(String... indices);
    
    /**
     * Waits for all pending cluster tasks to complete.
     * 
     * @param timeout maximum time to wait
     */
    public static void waitForPendingTasks(TimeValue timeout);
    
    /**
     * Returns the number of nodes in the cluster.
     * 
     * @return node count
     */
    public static int numNodes();
    
    /**
     * Returns settings that should be applied to all nodes in the cluster.
     * Override this method to customize node configuration.
     * 
     * @return settings for cluster nodes
     */
    protected Settings nodeSettings(int nodeOrdinal);
    
    /**
     * Returns additional plugins that should be installed on cluster nodes.
     * 
     * @return collection of plugin classes
     */
    protected Collection<Class<? extends Plugin>> nodePlugins();
}

ESIntegTestCase Usage Examples

import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;

@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 3)
public class MyIntegrationTest extends ESIntegTestCase {
    
    public void testIndexAndSearch() {
        // Create index with custom settings
        createIndex("test-index", Settings.builder()
            .put("number_of_shards", 2)
            .put("number_of_replicas", 1)
            .build());
            
        ensureGreen("test-index");
        
        // Index some documents
        for (int i = 0; i < 10; i++) {
            client().prepareIndex("test-index")
                .setId(String.valueOf(i))
                .setSource("field1", "value" + i, "field2", i)
                .get();
        }
        
        refresh("test-index");
        
        // Search and verify results
        SearchResponse response = client().prepareSearch("test-index")
            .setQuery(QueryBuilders.rangeQuery("field2").gte(5))
            .get();
            
        assertThat(response.getHits().getTotalHits().value, equalTo(5L));
    }
    
    public void testClusterHealth() {
        createIndex("health-test");
        
        // Wait for green status
        ensureGreen("health-test");
        
        // Verify cluster is healthy
        ClusterHealthResponse health = client().admin().cluster()
            .prepareHealth("health-test")
            .get();
            
        assertThat(health.getStatus(), equalTo(ClusterHealthStatus.GREEN));
        assertThat(health.getNumberOfDataNodes(), greaterThanOrEqualTo(1));
    }
    
    @Override
    protected Settings nodeSettings(int nodeOrdinal) {
        return Settings.builder()
            .put(super.nodeSettings(nodeOrdinal))
            .put("node.attr.custom", "value-" + nodeOrdinal)
            .build();
    }
    
    @Override
    protected Collection<Class<? extends Plugin>> nodePlugins() {
        return Arrays.asList(MyTestPlugin.class);
    }
}

SingleNodeTestCase

Lightweight integration testing base class that runs tests against a single Elasticsearch node. More efficient than full cluster tests when cluster features are not needed.

package org.elasticsearch.test;

import org.elasticsearch.client.internal.Client;
import org.elasticsearch.node.Node;

/**
 * Base class for integration tests that require only a single Elasticsearch node.
 * Provides faster test execution compared to full cluster tests while still offering
 * integration testing capabilities.
 */
public abstract class SingleNodeTestCase extends ESTestCase {
    
    /**
     * Returns the client connected to the single test node.
     * 
     * @return client for the test node
     */
    protected static Client client();
    
    /**
     * Returns the test node instance.
     * 
     * @return Node instance for direct node access
     */
    protected static Node node();
    
    /**
     * Creates an index with the specified name.
     * 
     * @param index index name
     */
    protected static void createIndex(String index);
    
    /**
     * Creates an index with custom settings and mappings.
     * 
     * @param index index name
     * @param settings index settings
     * @param mappings index mappings
     */
    protected static void createIndex(String index, Settings settings, String mappings);
    
    /**
     * Returns settings for the test node. Override to customize node configuration.
     * 
     * @return settings for the single test node
     */
    protected Settings nodeSettings();
    
    /**
     * Returns plugins to install on the test node.
     * 
     * @return collection of plugin classes
     */
    protected Collection<Class<? extends Plugin>> getPlugins();
}

SingleNodeTestCase Usage Example

import org.elasticsearch.test.SingleNodeTestCase;
import org.elasticsearch.action.get.GetResponse;

public class MySingleNodeTest extends SingleNodeTestCase {
    
    public void testBasicIndexing() {
        createIndex("single-node-test");
        
        // Index a document
        client().prepareIndex("single-node-test")
            .setId("1")
            .setSource("title", "Test Document", "content", "This is a test")
            .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
            .get();
        
        // Retrieve and verify
        GetResponse response = client().prepareGet("single-node-test", "1").get();
        
        assertTrue("Document should exist", response.isExists());
        assertThat(response.getSource().get("title"), equalTo("Test Document"));
    }
    
    @Override
    protected Settings nodeSettings() {
        return Settings.builder()
            .put(super.nodeSettings())
            .put("xpack.security.enabled", false)
            .put("xpack.monitoring.enabled", false)
            .build();
    }
}

TestCluster Interface

Abstract interface for cluster management used by integration test base classes.

package org.elasticsearch.test;

import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.node.DiscoveryNode;

/**
 * Interface for managing test clusters. Provides abstraction over cluster lifecycle
 * and client management for different cluster implementations.
 */
public interface TestCluster extends Closeable {
    
    /**
     * Returns a client connected to the cluster.
     * 
     * @return cluster client
     */
    Client client();
    
    /**
     * Returns the number of nodes in the cluster.
     * 
     * @return node count
     */  
    int size();
    
    /**
     * Returns the number of data nodes in the cluster.
     * 
     * @return data node count
     */
    int numDataNodes();
    
    /**
     * Returns the cluster name.
     * 
     * @return cluster name
     */
    String getClusterName();
    
    /**
     * Returns information about all nodes in the cluster.
     * 
     * @return iterable of discovery nodes
     */
    Iterable<DiscoveryNode> getNodes();
    
    /**
     * Starts the cluster if not already running.
     */
    void beforeTest(Random random, double transportClientRatio);
    
    /**
     * Performs cleanup after test execution.
     */
    void afterTest();
    
    /**
     * Wipes the cluster state clean between tests.
     */
    void wipe(Set<String> excludeTemplates);
}

Best Practices

Test Isolation

  • Always call super.setUp() and super.tearDown() in custom setup/teardown methods
  • Use randomized data to catch edge cases and improve test coverage
  • Keep tests independent - each test should work regardless of execution order

Performance Optimization

  • Use SingleNodeTestCase when cluster features are not needed
  • Minimize cluster scope - prefer Scope.TEST over Scope.SUITE when appropriate
  • Use @ClusterScope annotations to configure optimal cluster size

Resource Management

  • Properly clean up resources in tearDown methods
  • Use try-with-resources for clients and connections
  • Avoid resource leaks in long-running test suites

Randomization

  • Leverage built-in random data generators for comprehensive testing
  • Use consistent seeds for reproducible test failures
  • Combine deterministic assertions with randomized inputs

The core testing classes provide a solid foundation for all Elasticsearch testing scenarios, from simple unit tests to complex integration scenarios involving multiple nodes and cluster management.

Install with Tessl CLI

npx tessl i tessl/maven-org-elasticsearch--elasticsearch-test-framework

docs

assertions-matchers.md

cluster-management.md

core-testing.md

data-generation.md

index.md

mock-implementations.md

specialized-testing.md

tile.json