MQTT Testing Guide
Overview
Cuppet Core now includes comprehensive MQTT testing support, allowing you to test IoT devices, message brokers, and event-driven architectures using Cucumber BDD scenarios.
Features
✅ Hook-based Connection Management
✅ Topic Subscriptions
✅ Message Publishing
✅ QoS Support
✅ Message Validation
✅ Variable Storage
✅ Message Buffering
✅ Async Message Handling
-
Hook-based Connection Management
- Automatic connection/cleanup with
@mqtttag - Topic Subscriptions - Subscribe to topics with wildcard support (+, #)
- Message Publishing - Send text and JSON messages
- QoS Support - Configure Quality of Service levels (0, 1, 2)
- Message Validation - Assert message content, JSON properties, and types
- Variable Storage - Save and reuse message data across scenarios
- Message Buffering - Track all received messages per topic
- Async Message Handling - Wait for messages with configurable timeouts
Setup
Configure MQTT Broker
Add your MQTT broker configuration to
config/default.json
:
{
"mqtt": {
"brokerUrl": "mqtt://localhost:1883",
"username": "your_username",
"password": "your_password",
"clientId": "",
"cleanSession": true,
"connectTimeout": 5000,
"keepalive": 60
}
}
Configuration Options:
| Option | Description | Default |
|---|---|---|
brokerUrl |
MQTT broker URL (mqtt://, mqtts://, ws://, wss://) | mqtt://localhost:1883 |
username |
Authentication username |
""
(empty)
|
password |
Authentication password |
""
(empty)
|
clientId |
Custom client ID | Auto-generated |
cleanSession |
Start with a clean session | true |
connectTimeout |
Connection timeout in milliseconds | 5000 |
keepalive |
Keep-alive interval in seconds | 60 |
Usage
Basic Scenario Structure
Tag your scenarios with
@mqtt
to automatically connect to the broker:
@mqtt
Feature: Device Telemetry Testing
Scenario: Validate temperature sensor data
When I subscribe to MQTT topic "sensors/temperature"
And I publish JSON '{"temp": 25.5, "unit": "celsius"}' to MQTT topic "sensors/temperature"
Then I should receive a message on MQTT topic "sensors/temperature" within 5 seconds
And the MQTT message on topic "sensors/temperature" should have property "temp" with value "25.5"
Available Step Definitions
Subscription Steps
# Subscribe to a topic with QoS 0
When I subscribe to MQTT topic "devices/sensor1/data"
# Subscribe with custom QoS level
When I subscribe to MQTT topic "devices/+/telemetry" with QoS 1
# Unsubscribe from a topic
When I unsubscribe from MQTT topic "devices/sensor1/data"
Wildcard Support:
-
+- Single-level wildcard (e.g.,devices/+/status) -
#- Multi-level wildcard (e.g.,devices/#)
Publishing Steps
# Publish simple text message
When I publish "Hello MQTT" to MQTT topic "test/messages"
# Publish JSON message
When I publish JSON '{"status": "online", "deviceId": "sensor-001"}' to MQTT topic "devices/status"
# Publish with QoS and retain flag
When I publish "Critical Alert" to MQTT topic "alerts/critical" with QoS 2 and retain true
# Use saved variables in messages
When I publish "%savedDeviceId%" to MQTT topic "devices/%deviceId%/command"
Message Validation Steps
# Wait for a message (default 10 seconds)
Then I should receive a message on MQTT topic "test/response"
# Wait with custom timeout
Then I should receive a message on MQTT topic "test/response" within 30 seconds
# Validate exact message content
Then the MQTT message on topic "test/echo" should equal "Hello World"
# Validate message contains substring
Then the MQTT message on topic "logs/application" should contain "ERROR"
# Validate JSON property value
Then the MQTT message on topic "sensors/temp" should have property "temperature" with value "25"
# Validate nested JSON property
Then the MQTT message on topic "devices/data" should have property "sensor.reading.value" with value "100"
# Validate property type
Then the MQTT message on topic "sensors/data" property "temperature" should be a "number"
Then the MQTT message on topic "device/info" property "name" should be a "string"
Data Storage Steps
# Remember a JSON property value
Then I remember the MQTT message property "deviceId" from topic "devices/registration" as "savedDeviceId"
# Remember entire message
Then I remember the MQTT message from topic "test/data" as "lastMessage"
# Use saved variable in subsequent steps
When I publish "Command for %savedDeviceId%" to MQTT topic "devices/commands"
Buffer Management Steps
# Clear message buffer for specific topic
When I clear the MQTT message buffer for topic "test/messages"
# Clear all message buffers
When I clear all MQTT message buffers
# Validate message count
Then I should have received 5 messages on MQTT topic "test/counter"
Advanced Connection Steps
For scenarios requiring multiple brokers or custom connection handling:
# Connect to specific broker
Given I connect to MQTT broker "mqtt://test-broker.example.com:1883"
# Disconnect from broker
Given I disconnect from MQTT broker
Note:
These steps are optional when using the
@mqtt
tag, which handles connections automatically.
Examples
Example 1: Simple Echo Test
@mqtt
Scenario: Test MQTT echo service
When I subscribe to MQTT topic "test/echo"
And I publish "Hello MQTT World" to MQTT topic "test/echo"
Then I should receive a message on MQTT topic "test/echo" within 5 seconds
And the MQTT message on topic "test/echo" should equal "Hello MQTT World"
Example 2: IoT Device Registration
@mqtt
Scenario: Register new IoT device
Given I subscribe to MQTT topic "devices/registration/response"
When I publish JSON '{"deviceType": "sensor", "location": "warehouse"}' to MQTT topic "devices/registration/request"
Then I should receive a message on MQTT topic "devices/registration/response" within 10 seconds
And the MQTT message on topic "devices/registration/response" should have property "status" with value "success"
And I remember the MQTT message property "deviceId" from topic "devices/registration/response" as "newDeviceId"
# Use the saved device ID
When I publish "ping" to MQTT topic "devices/%newDeviceId%/command"
Example 3: Wildcard Subscriptions
@mqtt
Scenario: Monitor all device telemetry
When I subscribe to MQTT topic "devices/+/telemetry"
And I publish JSON '{"temp": 22.5}' to MQTT topic "devices/sensor1/telemetry"
And I publish JSON '{"temp": 23.0}' to MQTT topic "devices/sensor2/telemetry"
Then I should receive a message on MQTT topic "devices/sensor1/telemetry" within 5 seconds
And I should receive a message on MQTT topic "devices/sensor2/telemetry" within 5 seconds
Example 4: Message Count Validation
@mqtt
Scenario: Validate burst message handling
Given I subscribe to MQTT topic "test/burst"
And I clear the MQTT message buffer for topic "test/burst"
When I publish "Message 1" to MQTT topic "test/burst"
And I publish "Message 2" to MQTT topic "test/burst"
And I publish "Message 3" to MQTT topic "test/burst"
And I wait for "1" seconds
Then I should have received 3 messages on MQTT topic "test/burst"
Example 5: Complex JSON Validation
@mqtt
Scenario: Validate sensor telemetry structure
When I subscribe to MQTT topic "sensors/environment"
And I publish JSON '{"sensor": {"id": "env-001", "readings": {"temp": 25.5, "humidity": 60}}}' to MQTT topic "sensors/environment"
Then I should receive a message on MQTT topic "sensors/environment" within 5 seconds
And the MQTT message on topic "sensors/environment" should have property "sensor.id" with value "env-001"
And the MQTT message on topic "sensors/environment" property "sensor.readings.temp" should be a "number"
And the MQTT message on topic "sensors/environment" property "sensor.readings.humidity" should be a "number"
Example 6: API + MQTT Combined Testing
@mqtt @api
Scenario: Test end-to-end device workflow
# Register device via API
Given I set the request body to '{"deviceName": "Sensor-X", "type": "temperature"}'
When I send a "POST" request to "/api/devices"
Then the response code should be "201"
And I remember the value of the "id" property as "deviceId"
# Subscribe to device topic
When I subscribe to MQTT topic "devices/%deviceId%/data"
# Simulate device sending data
When I publish JSON '{"temperature": 28.5, "timestamp": 1234567890}' to MQTT topic "devices/%deviceId%/data"
# Validate MQTT message
Then I should receive a message on MQTT topic "devices/%deviceId%/data" within 5 seconds
And the MQTT message on topic "devices/%deviceId%/data" should have property "temperature" with value "28.5"
Testing Different MQTT Brokers
Mosquitto (Local)
{
"mqtt": {
"brokerUrl": "mqtt://localhost:1883"
}
}
HiveMQ Cloud
{
"mqtt": {
"brokerUrl": "mqtts://your-cluster.s1.eu.hivemq.cloud:8883",
"username": "your_username",
"password": "your_password"
}
}
AWS IoT Core
{
"mqtt": {
"brokerUrl": "mqtts://your-endpoint.iot.us-east-1.amazonaws.com:8883",
"clientId": "test-client",
"cert": "/path/to/cert.pem",
"key": "/path/to/private.key",
"ca": "/path/to/root-CA.crt"
}
}
EMQX (Local Docker)
# Start EMQX broker
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx:latest
# Configure
{
"mqtt": {
"brokerUrl": "mqtt://localhost:1883",
"username": "admin",
"password": "public"
}
}
Programmatic Usage
You can also use MQTT functions programmatically in your code:
const { mqttFunctions, MqttManager } = require('@cuppet/core');
// Create and initialize MQTT manager
const mqttManager = new MqttManager('mqtt://localhost:1883');
await mqttManager.initialize();
// Set the manager in functions
mqttFunctions.setMqttManager(mqttManager);
// Use MQTT functions
await mqttFunctions.subscribeToTopic('test/topic');
await mqttFunctions.publishMessage('Hello', 'test/topic');
const message = await mqttFunctions.waitForMessage('test/topic', 5);
// Cleanup
await mqttManager.stop();
Troubleshooting
Connection Issues
- Check broker URL and credentials
- Verify broker is running
- Check firewall/network settings
- Ensure correct protocol (mqtt:// vs mqtts://)
Messages Not Received
- Verify subscription before publishing
- Check topic spelling (case-sensitive)
- Increase timeout if network is slow
- Verify QoS levels match requirements
Tips
- Check message buffer: Messages are stored per topic
-
Use wildcards carefully:
+vs#have different behaviors - Clear buffers: Between tests to avoid stale data
Best Practices
-
Use
@mqtttag: Let hooks manage connections automatically - Subscribe before publishing: Ensure you're listening before messages arrive
- Clear buffers: Between related tests to avoid cross-test pollution
-
Use meaningful topics:
Follow topic naming conventions (e.g.,
domain/device/datatype) - Set appropriate timeouts: Balance between reliability and test speed
- Use QoS wisely: QoS 0 for most tests, higher levels when delivery guarantee is tested
- Variable storage: Save dynamic IDs and reuse them across steps
Architecture
The MQTT implementation follows Cuppet's existing patterns:
-
features/app/managers/mqttManager.js: Connection lifecycle management (like BrowserManager) -
src/mqttFunctions.js: Core MQTT operations (like apiFunctions) -
features/app/stepDefinitions/mqttSteps.js: Cucumber step definitions -
features/app/hooks.js: Automatic connection via@mqtttag
This design ensures consistency with your existing testing framework!