Accumulo has several tools that can help developers test their code.

MiniAccumuloCluster

MiniAccumuloCluster is a standalone instance of Apache Accumulo for testing. It will create Zookeeper and Accumulo processes that write all of their data to a single local directory. MiniAccumuloCluster makes it easy to code against a real Accumulo instance. Developers can write realistic-to-end integration tests that mimic the use of a normal Accumulo instance.

MiniAccumuloCluster is published in a separate jar that should be added to your pom.xml as a test dependency:

  1. <dependency>
  2. <groupId>org.apache.accumulo</groupId>
  3. <artifactId>accumulo-minicluster</artifactId>
  4. <version>${accumulo.version}</version>
  5. <scope>test</scope>
  6. </dependency>

To start it up, you will need to supply an empty directory and a root password as arguments:

  1. File tempDirectory = // JUnit and Guava supply mechanisms for creating temp directories
  2. MiniAccumuloCluster mac = new MiniAccumuloCluster(tempDirectory, "password");
  3. mac.start();

Once we have our mini cluster running, we will want to interact with the Accumulo client API:

  1. AccumuloClient client = mac.getAccumuloClient("root", new PasswordToken("password"));

Upon completion of our development code, we will want to shutdown our MiniAccumuloCluster:

  1. mac.stop();
  2. // delete your temporary folder

Iterator Test Harness

Iterators, while extremely powerful, are notoriously difficult to test. While the API defines the methods an Iterator must implement and each method’s functionality, the actual invocation of these methods by Accumulo TabletServers can be surprisingly difficult to mimic in unit tests.

The Apache Accumulo “Iterator Test Harness” is designed to provide a generalized testing framework for all Accumulo Iterators to leverage to identify common pitfalls in user-created Iterators.

Framework Use

The Iterator Test Harness is published in a separate jar that should be added to your pom.xml as a test dependency:

  1. <dependency>
  2. <groupId>org.apache.accumulo</groupId>
  3. <artifactId>accumulo-iterator-test-harness</artifactId>
  4. <version>${accumulo.version}</version>
  5. <scope>test</scope>
  6. </dependency>

To use the Iterator test harness, create a class that extends the IteratorTestBase class and defines the following:

  • A SortedMap of input data (Key-Value pairs)
  • A Range to use in tests
  • A Map of options (String to String pairs)
  • A SortedMap of output data (Key-Value pairs)
  • A list of IteratorTestCases (these can be automatically discovered)

The majority of effort a user must make is in creating the input dataset and the expected output dataset for the iterator being tested.

Normal Test Outline

Most iterator tests will follow the given outline:

  1. import java.util.List;
  2. import java.util.SortedMap;
  3. import org.apache.accumulo.core.data.Key;
  4. import org.apache.accumulo.core.data.Range;
  5. import org.apache.accumulo.core.data.Value;
  6. import org.apache.accumulo.iteratortest.IteratorTestBase;
  7. import org.apache.accumulo.iteratortest.IteratorTestInput;
  8. import org.apache.accumulo.iteratortest.IteratorTestOutput;
  9. import org.apache.accumulo.iteratortest.IteratorTestParameters;
  10. public class MyIteratorTest extends IteratorTestBase {
  11. @Override
  12. protected Stream<IteratorTestParameters> parameters() {
  13. var input = new IteratorTestInput(MyIterator.class, Map.of(), createRange(), INPUT_DATA);
  14. var expectedOutput = new IteratorTestOutput(OUTPUT_DATA);
  15. return builtinTestCases().map(test -> test.toParameters(input, expectedOutput));
  16. }
  17. private static SortedMap<Key,Value> INPUT_DATA = createInputData();
  18. private static SortedMap<Key,Value> OUTPUT_DATA = createOutputData();
  19. private static SortedMap<Key,Value> createInputData() {
  20. // TODO -- implement this method
  21. }
  22. private static SortedMap<Key,Value> createOutputData() {
  23. // TODO -- implement this method
  24. }
  25. private static Map<String,String> createOpts() {
  26. IteratorSetting setting = new IteratorSetting(50, MyIterator.class);
  27. // TODO -- add iterator specific options
  28. return setting.getOptions();
  29. }
  30. private static Range createRange() {
  31. // TODO -- implement this method
  32. }
  33. }

Limitations

While the classes that implement IteratorTestCases should exercise common edge-cases in user iterators, there are still many limitations to the existing test harness. Some of them are:

  • Can only specify a single iterator, not many (a “stack”)
  • No control over provided IteratorEnvironment for tests
  • Exercising delete keys (especially with major compactions that do not include all files)

These are left as future improvements to the harness.