HiveMQ Community Edition in a test container

Written by Yannick Weber

Category: HiveMQ Testing HiveMQ MQTT Client MQTT Client

Published: March 17, 2020


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.

JUnit 4

First, add the following dependencies to your classpath:

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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. You can find the code for the JUnit 4 and JUnit5 example over at GitHub.

About Yannick Weber

Yannick is a software developer at HiveMQ. He mainly works on expanding the HiveMQ Extension System.
Contact Yannick

HiveMQ 3.4.6 released
Implementing MQTT Challenge-Response Authentication