HiveMQ Community Edition in a test container

HiveMQ Community Edition in a test container

author Yannick Weber

Written by Yannick Weber

Category: HiveMQ Testing Test Container 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. 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:

 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.

author Yannick Weber

About Yannick Weber

Yannick is a Senior Software Engineer and one of the core members of HiveMQ’s product development team. He 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.

mail icon Contact Yannick
newer posts Authentication of IoT devices with LDAP
Implementing MQTT Challenge-Response Authentication older posts