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.