Skip to content

HiveMQ Community Edition in a test container

by Yannick Weber
3 min read

Testing your MQTT client application with a HiveMQ Community Edition test container

Testing your MQTT client application is not a simple task, since you have to have a running MQTT broker in your testing environment. Sharing your broker deployment between multiple tests can lead to unwanted interferences, unexpected behaviour and flaky tests. The solution is to start a containerized HiveMQ broker that you use exclusively for each test and destroy afterwards. This blog post shows you how to do this automatically in JUnit 4 and JUnit 5 with the help of the Testcontainers Project. If you are new to MQTT protocol, I recommend you to read MQTT Essentials here.

JUnit 4

First, add the following dependencies to your classpath:

Next, add this class that extends the JUnit 4 TestWatcher to your testing environment:

public class HiveMQTestContainerRule extends TestWatcher {

    private static final @NotNull String HIVEMQ_CE_IMAGE = "hivemq/hivemq-ce";
    private static final @NotNull String HIVEMQ_CE_VERSION = "latest";
    public static final int MQTT_PORT = 1883;

    private final @NotNull GenericContainer container;

    public HiveMQTestContainerRule() {
        container = new GenericContainer(HIVEMQ_CE_IMAGE + ":" + HIVEMQ_CE_VERSION);
        container.withExposedPorts(MQTT_PORT);

        final LogMessageWaitStrategy waitStrategy = new LogMessageWaitStrategy();
        waitStrategy.withRegEx(".*Started HiveMQ in.*");
        container.waitingFor(waitStrategy);
    }

    @Override
    protected void starting(final @NotNull Description description) {
        container.start();
    }

    @Override
    protected void finished(final @NotNull Description description) {
        container.stop();
    }

    public int getMqttPort() {
        return container.getMappedPort(MQTT_PORT);
    }
}

You can use the @Rule annotation to add the HiveMQTestContainerRule to every JUnit 4 test class that you want. The MQTT port can be retrieved with the getMqttPort method.

public class MqttClientTestWithContainerRule {

    @Rule
    final public @NotNull HiveMQTestContainerRule rule = new HiveMQTestContainerRule();

    @Test
    public void test_mqtt() {
        final Mqtt5BlockingClient client = Mqtt5Client.builder()
                .serverPort(rule.getMqttPort())
                .buildBlocking();

        client.connect();
        client.disconnect();
    }
}

JUnit 5

First, add the following dependencies to your classpath:

Next, add this class that implements the JUnit5 BeforeEachCallback and AfterEachCallback to your testing environment:

public class HiveMQTestContainerExtension implements BeforeEachCallback, AfterEachCallback {

    private static final @NotNull String HIVEMQ_CE_IMAGE = "hivemq/hivemq-ce";
    private static final @NotNull String HIVEMQ_CE_VERSION = "latest";
    public static final int MQTT_PORT = 1883;

    private final @NotNull GenericContainer container;

    public HiveMQTestContainerExtension() {
        container = new GenericContainer(HIVEMQ_CE_IMAGE + ":" + HIVEMQ_CE_VERSION);
        container.withExposedPorts(MQTT_PORT);

        final LogMessageWaitStrategy waitStrategy = new LogMessageWaitStrategy();
        waitStrategy.withRegEx(".*Started HiveMQ in.*");
        container.waitingFor(waitStrategy);
    }

    @Override
    public void beforeEach(final @NotNull ExtensionContext context) throws Exception {
        container.start();
    }

    @Override
    public void afterEach(final @NotNull ExtensionContext context) throws Exception {
        container.stop();
    }

    public int getMqttPort() {
        return container.getMappedPort(MQTT_PORT);
    }
}

You can use the @RegisterExtension annotation to add the HiveMQTestContainerExtension to every JUnit 5 test class as desired. The MQTT port can be retrieved with the getMqttPort method.

public class MqttClientTestWithContainerExtension {

    @RegisterExtension
    public final @NotNull HiveMQTestContainerExtension extension = new HiveMQTestContainerExtension();

    @Test
    void test_mqtt() {
        final Mqtt5BlockingClient client = Mqtt5Client.builder()
                .serverPort(extension.getMqttPort())
                .buildBlocking();

        client.connect();
        client.disconnect();
    }
}

Conclusion

These methods make it very easy to automate the process of starting a fresh MQTT broker. The development and testing process for your client applications becomes more robust when you use use a containerized, isolated, and exclusive broker for every test.

Yannick Weber

Yannick is a Senior Software Engineer and one of the core members of HiveMQ's product development team. He has a strong interest in messaging technologies and is focusing on quality development of HiveMQ's many tools and extensions. In addition, he is the maintainer of the HiveMQ module in the testcontainers-java project.

  • Contact Yannick Weber via e-mail

Related content:

HiveMQ logo
Review HiveMQ on G2