This is a prerelease version.

View latest

Common test utilities

This section describes Hazelcast’s common test utilities across caching and streaming applications.

Repeat tests

Use @Repeat(n) (com.hazelcast.test.annotation.Repeat) to repeat a test multiple times. This is useful to execute a test multiple times to trigger flaky behavior. The test passes only if all repeated executions succeed.

@Repeat(5)
@Test
public void testFlakyBehaviour() {
    // ...
}

Test runners

Hazelcast provides custom runners:

  • HazelcastSerialClassRunner: runs tests sequentially.

  • HazelcastParallelClassRunner: runs tests in parallel.

@RunWith(HazelcastParallelClassRunner.class)
public class MyClusterTest extends HazelcastTestSupport {
    // ...
}

Tests using these runners may fail if some other test doesn’t shutdown client or member instances correctly.

Isolate clusters by name

Use randomName() to avoid interference when tests run in parallel.

String clusterName = HazelcastTestSupport.randomName();
Config config = new Config().setClusterName(clusterName);
HazelcastInstance[] members = factory.newInstances(config, 2);

ClientConfig clientConfig = new ClientConfig().setClusterName(clusterName);
HazelcastInstance client = factory.newHazelcastClient(clientConfig);

Clean up resources

At the end of each test or test class, use shutdown() on individual Hazelcast instances, or a factory’s shutdownAll() method to clean up all Hazelcast instances.

JUnit 4:

@After
public void tearDown() {
    client.shutdown();
    member1.shutdown();

    // or
    factory.shutdownAll();
}

JUnit 5:

@AfterAll
static void tearDownCluster() {
    client.shutdown();
    member1.shutdown();

    // or
    factory.shutdownAll();
}

Shutting down a single instance via ìnstance.shutdown() will trigger a graceful shutdown. The method instance.getLifecycleService().terminate() can be used to trigger an ungraceful termination, which can be used to mimic failure scenarios.

Test distributed logic

It’s possible to test distributed logic with a combination of Hazelcast test support and mock objects. This is useful to test the wiring of callbacks.

Test map events

For example, consider an event listener on an IMap to illustrate the pattern. You can test the invocation of the mocked listener and verify its invocation.

import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.map.listener.EntryUpdatedListener;
import com.hazelcast.test.HazelcastTestSupport;
import org.junit.Test;

import static org.mockito.Mockito.*;

public class MyMapListenerTest extends HazelcastTestSupport {

    @Test
    public void testUpdateTriggersListener() {
        // create Hazelcast member
        HazelcastInstance instance = createHazelcastInstance();

        // create mock listener
        EntryUpdatedListener<String, String> mockListener = mock(EntryUpdatedListener.class);

        // register the listener
        IMap<String, String> map = instance.getMap("test-map");
        map.addEntryListener(mockListener, true);

        // insert and update an entry
        map.put("key1", "initial");
        map.put("key1", "updated");

        // verify the listener received the update
        verify(mockListener, timeout(1000).times(1)).entryUpdated(any(EntryEvent.class));
    }
}

The following example tests the invocation of an asynchronous MapStore. When using Hazelcast IMap with MapStore, entries can be loaded from or persisted to an external data source. This example shows how to validate the integration between IMap and a MapStore using a real backing store and TestHazelcastFactory in a JUnit 5 context.

import com.hazelcast.config.Config;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.MapStore;
import com.hazelcast.test.TestHazelcastFactory;
import org.junit.jupiter.api.*;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class MyMapStoreTest {

    private static TestHazelcastFactory factory;
    private static HazelcastInstance hz;

    @BeforeAll
    static void setup() {
        factory = new TestHazelcastFactory();

        // Create a mock MapStore that throws on load
        @SuppressWarnings("unchecked")
        MapStore<String, String> mockMapStore = mock(MapStore.class);
        when(mockMapStore.load("fail")).thenThrow(new RuntimeException("Simulated failure"));
        when(mockMapStore.load("key1")).thenReturn("value1");

        // Configure Hazelcast to use the mock MapStore
        Config config = new Config();
        config.setClusterName(randomName());
        config.getMapConfig("testMap")
              .getMapStoreConfig()
              .setEnabled(true)
              .setImplementation(mockMapStore);

        hz = factory.newHazelcastInstance(config);
    }

    @AfterAll
    static void teardown() {
        if (factory != null) {
            factory.shutdownAll();
        }
    }

    @Test
    void testSuccessfulLoadFromMock() {
        var map = hz.getMap<String, String>("testMap");

        // This triggers MapStore.load("key1")
        String result = map.get("key1");
        assertEquals("value1", result);
    }

    @Test
    void testLoadFailureHandled() {
        var map = hz.getMap<String, String>("testMap");

        RuntimeException ex = assertThrows(RuntimeException.class, () -> {
            map.get("fail"); // triggers MapStore.load("fail")
        });

        assertThatThrownBy(() ->  map.get("fail"))
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Simulated failure");
    }

    @Test
    void testStoreIsInvoked() {
        @SuppressWarnings("unchecked")
        MapStore<String, String> mockMapStore = mock(MapStore.class);

        // Configure and start another instance for the store test
        Config config = new Config().setClusterName("store-test");
        config.getMapConfig("storeMap")
              .getMapStoreConfig()
              .setEnabled(true)
              .setImplementation(mockMapStore);

        HazelcastInstance storeHz = factory.newHazelcastInstance(config);
        var storeMap = storeHz.getMap<String, String>("storeMap");

        storeMap.put("k2", "v2");

        // Verify that store was called
        verify(mockMapStore, timeout(1000)).store("k2", "v2");

        storeHz.shutdown();
    }
}

Test member failure scenarios

Application logic that handles scenarios where nodes fail or the cluster size changes can be tested by explicitly calling members' shutdown. In this example, we can capture and test the logic of a listener (mocked here for simplicity) programmed to react to a member being stopped:

public class MyClusterFailureTest {

    private HazelcastInstance client;
    private HazelcastInstance member1;
    private HazelcastInstance member2;
    private MembershipListener mockListener;

    @BeforeEach
    void setupCluster() {
        TestHazelcastFactory factory = new TestHazelcastFactory(2);
        member1 = factory.newHazelcastInstance(getConfig("1"));
        member2 = factory.newHazelcastInstance(getConfig("2"));

        ClientConfig clientConfig = new ClientConfig();
        mockListener = mock(MembershipListener.class);
        ListenerConfig listenerConfig = new ListenerConfig(mockListener);
        listenerConfig.setImplementation(mockListener);
        clientConfig.addListenerConfig(listenerConfig);
        client = factory.newHazelcastClient(clientConfig);
    }

    private static Config getConfig(String v) {
        MemberAttributeConfig mAttr = new MemberAttributeConfig();
        mAttr.setAttribute("m", v);
        Config config = new Config();
        config.setMemberAttributeConfig(mAttr);
        return config;
    }

    @AfterEach
    void tearDownCluster() {
        if (client != null) {
            client.shutdown();
        }
        if (factory != null) {
            factory.shutdownAll();
        }
    }

    @Test
    public void testClusterFailure() {
        assertClusterSizeEventually(2, client);
        member1.getMap("testMap").put("key1", "value1");
        assertEqualsEventually(() -> client.getMap("testMap").get("key1"), "value1");
        // for graceful shutdown
        member1.shutdown();
        // for abrupt termination
        // member1.getLifecycleService().terminate()
        assertClusterSizeEventually(1, client);
        member1 = null;
        assertEqualsEventually(() -> client.getMap("testMap").get("key1"), "value1");
        ArgumentCaptor<MembershipEvent> membershipCaptor = ArgumentCaptor.forClass(MembershipEvent.class);
        verify(mockListener).memberRemoved(membershipCaptor.capture());
        MembershipEvent membershipEvent = membershipCaptor.getValue();
        assertEqualsEventually(() -> membershipEvent.getMember().getAttribute("m"), "1");
    }
}