
How to Test MQTT Client Applications

Written by Yannick Weber
Category: HiveMQ Testing Test Container MQTT Client
Published: April 11, 2022
Automatic testing of MQTT client applications is a challenging task since an MQTT Broker deployment of some kind is required. If you use a locally installed MQTT Broker for testing, you can not be sure that the tests run with the same results in a different environment. For example, when the tests are run on the machine of a coworker or a continuous integration environment. When you use a public MQTT broker, it is always possible for other clients to interfere with your test. On top of that, you are fully dependent on the uptime of the public MQTT broker: if the broker is unavailable, your tests fail. The solution to this problem is to automatically start an exclusive MQTT broker for each integration test and destroy it afterwards. This solution can be realized with the help of the official HiveMQ Testcontainers Module.
Requirements
- MQTT application to test
- Docker for running HiveMQ containers
- Java libraries
- JUnit 4 or JUnit 5 as a testing framework
- HiveMQ Testcontainer for starting and stopping HiveMQ containers
- Mockito as a mocking framework
- HiveMQ MQTT Client to create test data
Example MQTT application
As an example, we can test the implementation of the MQTT client that is embedded inside a SmartAquarium. The implementation has the following functionality that needs to be tested:
- Light: can be turned on and off by MQTT messages with the topic ’equipment/light’ and the payload ‘ON’ or ‘OFF’.
- CO2 injection: can be turned on and off by MQTT messages with the topic ’equipment/co2’ and the payload ‘ON’ or ‘OFF’.
- Pump: can be turned on and off by MQTT messages with the topic ’equipment/pump’ and the payload ‘ON’ or ‘OFF’.
- Temperature Sensor: measures the water temperature and publishes it with the topic ‘status/temperature’.
The java implementation of the SmartAquariumClient looks something like this:
|
|
The SmartAquariumClient
uses the Eclipse Paho client internally and shows the following behavior:
- creates an eclipse paho client with the given broker Uri and client identifier ‘smartaquarium’ (1)
- registers itself as Mqtt callback for processing incoming publishes (2)
- connects to the broker (3)
- subscribes to the topics for Light, CO2, and Pump control (4)
When publishTemperature()
is called the SmartAquariumClient
- obtains the temperature from the
TemperatureSensor
(5) - publishes the temperature to the topic ‘status/temperature’ (6)
When a publish message is received the SmartAquariumClient
- retrieves the payload and converts it into an UTF-8 string (7)
- assigns the message to the respective topics of the equipment pieces (8)
- decides whether the specific device should be switched off or on (9)
The Light
, CO2
, Pump
and TemperatureSensor
interfaces are passed as constructor parameters, so the
SmartAquariumClient
is not concerned with their implementation and object-lifecycle. The client only knows about their
interface methods turnOn()
, turnOff()
and getCelsius()
. This neat decoupling induces great testability and allows
us to write robust and elegant tests.
Testing the MQTT application
For testing your MQTT client application you add the following dependencies to your build.gradle
:
|
|
Test MQTT message processing
In our test we want to ensure that the SmartAquariumClient
interacts with the Pump, CO2 and Light correctly when the
respective MQTT messages are received.
|
|
Steps for testing:
- Register the
HiveMQContainer
. (1) - Build and connect the
testClient
using the port obtained from the container. (2) - Register a timeout of 2 minutes for the test. (3)
- Since
Light
,Co2
,Pump
, andTemperatureSensor
are passed as constructor parameters, they can be mocked and handed toSmartAquariumClient
. (4) - Publish the signal ‘ON’ with the topic ’equipment/light’ with the
testClient
. (5) - Verify that the
turnedOn()
method of theLight
is called exactly once. To avoid a race condition, a timeout must be set that waits for the interaction for a specific amount of time. (6)
Test MQTT message publishing
In the next test, we want to ensure that the publishing of the temperature is working as expected.
|
|
Steps for testing:
- Register the
HiveMQContainer
. (1) - Build and connect the test client using the port obtained from the container. (2)
- Register a timeout of 2 minutes for the test. (3)
- Since
Light
,Co2
,Pump
, andTemperatureSensor
are passed as constructor parameters, they can be mocked and handed toSmartAquariumClient
. (4) - Subscribe the
testClient
to the topic where the temperature should be published (5) - Listen for publishes with the
testClient
(6) - Use Mockito to make the
TemperatureSensor.getCelsius()
return ‘13.0f’ (7) - Make the
SmartAquariumClient
publish the temperature (8) - Receive the message with the
testClient
using a blocking call to avoid a race condition here (9) - Assert expected results (10)
Conclusion
The following best practices where identified:
- Design your MQTT client application with testing in mind.
- Use an explicit timeout in every test so an unresponsive test does not block the entire pipeline.
- Think about concurrency to avoid race conditions in your tests.
You can find the code for the example over at GitHub.

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.
What is the best way to ingest IoT data to Microsoft Azure?