MQTT Fundamentals

MQTT Testing Checklist: 12 Checks to Run Before Go-Live

A go-live MQTT testing checklist with quick, repeatable tests for reconnects, QoS, retained messages, Last Will, payload validation, limits and load, all runnable against a free public broker.

Clipboard with ticked items next to a broker diagram and connected devices, illustrating an MQTT testing checklist
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

This MQTT testing checklist walks you through the twelve checks that catch most production incidents in MQTT applications before your users do. Each item comes with a quick test you can run from a laptop against a public broker, so by the end you will have a repeatable go-live routine instead of a vague feeling that "it worked on my desk".

Why MQTT Applications Need Their Own Test Plan

Unit tests cover your parsing and business logic, but most MQTT failures happen in the space between the client and the broker: a device that never resubscribes after a reconnect, a dashboard that shows a stale retained value, a Last Will that fires for the wrong reason, or a payload that is suddenly 70 KB because someone added a debug field. None of these show up in a happy-path demo.

The good news is that MQTT behavior is observable. Every message passes through the broker, so with two terminals and a subscriber on a wildcard you can see exactly what your devices and services do. If you need a refresher on the moving parts, start with how MQTT works and come back here.

Set Up a Test Environment First

Use a disposable environment for these checks. The public MQTT test broker at broker.connectmq.com accepts anonymous connections with any non-empty, unique client ID. Pick a random topic prefix so your tests do not collide with anyone else, and remember the public area is readable by everyone, so never send real credentials or customer data.

# Pick a unique prefix for this test session
export PREFIX="connectmq/demo/$(openssl rand -hex 4)"
echo $PREFIX

# Terminal 1: watch everything under your prefix, verbose, MQTT 5
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
  -i watcher-$RANDOM -t "$PREFIX/#" -v

Keep that watcher open for the whole session.

Know the public broker limits before you start, because some checks deliberately hit them: 64 KB per message, 10 messages per second per client, 10 subscriptions per client, 8 topic levels and a maximum session expiry of 30 minutes. Your production broker will have different limits, which is exactly why you should test them explicitly.

The MQTT Testing Checklist

Work through the list in order. Items 1 to 4 are about connectivity, 5 to 8 about message semantics, and 9 to 12 about robustness.

  1. Connection and authentication. Wrong password, wrong port and TLS failures produce clear errors.
  2. Client ID uniqueness. Two clients with the same ID do not silently kick each other in a loop.
  3. Reconnect and resubscribe. After a network drop the client reconnects with backoff and restores its subscriptions.
  4. Keep alive. A half-open connection is detected within roughly 1.5 times the keep alive interval.
  5. QoS behavior. The QoS you request is the QoS you get, and duplicates are handled.
  6. Retained state. New subscribers receive the right last-known value, and stale retained messages can be cleared.
  7. Last Will and Testament. An ungraceful disconnect produces an offline status; a clean shutdown does not.
  8. Persistent sessions. Messages published while a subscriber is offline arrive when it returns (QoS 1 and 2 only).
  9. Payload validation. Malformed JSON, missing fields and wrong types are rejected without crashing the consumer.
  10. Size, rate and topic limits. Oversized payloads, publish bursts and deep topics fail in a predictable way.
  11. Topic and wildcard design. Subscriptions match exactly what you intend and nothing more.
  12. Load and soak. The system survives realistic connection counts and message rates for hours, not seconds.

The sections below show how to run each group.

Connectivity Checks (Items 1 to 4)

Start with the negative cases. A good client logs a meaningful reason when it cannot connect; a bad one retries silently forever.

# Plain TCP should succeed
mosquitto_pub -h broker.connectmq.com -p 1883 -i test-pub-$RANDOM \
  -t "$PREFIX/ping" -m "hello"

# TLS on 8883 should succeed and validate the Let's Encrypt certificate
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -i test-tls-$RANDOM -t "$PREFIX/ping" -m "hello over tls"

# Plain MQTT against the TLS port should fail fast, not hang
mosquitto_pub -h broker.connectmq.com -p 8883 -i test-bad-$RANDOM \
  -t "$PREFIX/ping" -m "should fail"

For item 2, start two subscribers with the same client ID. The broker disconnects the older session when the newer one connects. If both clients auto-reconnect, they will take turns kicking each other off, which shows up as a rapid connect and disconnect pattern in your logs. Generate client IDs from a stable device identifier plus a role, never from a hard-coded constant shared across a fleet.

For item 3, the most reliable test is physical: run your client, then pull the network cable or disable Wi-Fi for 60 seconds. When the link returns, publish a message from the watcher terminal and confirm your client still receives it. Many libraries reconnect automatically but only restore subscriptions if you subscribe inside the on-connect callback. The Python script below shows the pattern and prints every state change so you can see what happens.

import time
import uuid
import paho.mqtt.client as mqtt

PREFIX = "connectmq/demo/replace-me"
client_id = f"reconnect-test-{uuid.uuid4().hex[:8]}"

def on_connect(client, userdata, flags, reason_code, properties):
    print(f"{time.strftime('%X')} connected: {reason_code}")
    # Subscribing here means subscriptions come back after every reconnect
    client.subscribe(f"{PREFIX}/cmd/#", qos=1)

def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
    print(f"{time.strftime('%X')} disconnected: {reason_code}")

def on_message(client, userdata, msg):
    print(f"{time.strftime('%X')} {msg.topic} qos={msg.qos} retain={msg.retain} {msg.payload!r}")

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id,
                     protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
client.reconnect_delay_set(min_delay=1, max_delay=30)

client.connect("broker.connectmq.com", 1883, keepalive=20)
client.loop_forever(retry_first_connection=True)

With a keep alive of 20 seconds, the broker should treat the client as gone roughly 30 seconds after traffic stops. That is item 4: if your device takes several minutes to notice a dead link, your keep alive is too long or the library is not sending PINGREQ.

Message Semantics Checks (Items 5 to 8)

QoS is negotiated per subscription and per publish, and the effective delivery level is the lower of the two. Test it explicitly rather than assuming. If you are unsure what each level guarantees, read MQTT QoS levels explained first.

# Subscribe at QoS 1, publish at QoS 2: the delivery is downgraded to QoS 1
mosquitto_sub -h broker.connectmq.com -i qos-sub-$RANDOM -q 1 \
  -t "$PREFIX/qos" -v -d

mosquitto_pub -h broker.connectmq.com -i qos-pub-$RANDOM -q 2 \
  -t "$PREFIX/qos" -m "qos test" -d

The -d flag prints the packet flow (PUBLISH, PUBACK, PUBREC and so on), which makes the downgrade visible. Your consumer code should also tolerate duplicates at QoS 1: process the same message twice and confirm nothing breaks, for example by keying writes on a message ID inside the payload.

For retained state, publish a retained value, then connect a brand-new subscriber and confirm it receives it immediately with the retain flag set. Then clear it by publishing an empty retained payload and confirm the next new subscriber gets nothing.

mosquitto_pub -h broker.connectmq.com -i ret-$RANDOM -r \
  -t "$PREFIX/sensor1/state" -m '{"temp":21.4}'

mosquitto_sub -h broker.connectmq.com -i ret-check-$RANDOM \
  -t "$PREFIX/sensor1/state" -v -C 1 -W 5

# Clear the retained message
mosquitto_pub -h broker.connectmq.com -i ret-clear-$RANDOM -r -n \
  -t "$PREFIX/sensor1/state"

The Last Will check needs a client that dies ungracefully. Connect with a will message, then kill the process with kill -9 or pull the network. The watcher should see the will after the keep alive expires. Repeat with a clean Ctrl+C exit that sends DISCONNECT: the will must not be published. The Last Will and Testament guide explains the retained online/offline pattern most fleets use.

mosquitto_sub -h broker.connectmq.com -i lwt-dev-$RANDOM -k 10 \
  --will-topic "$PREFIX/dev1/status" --will-payload offline --will-retain \
  -t "$PREFIX/dev1/cmd"

For persistent sessions, connect a subscriber with a fixed client ID, QoS 1 and a session expiry, disconnect it, publish three QoS 1 messages, then reconnect with the same ID. All three should arrive. On the public broker keep the session expiry at or under 1,800 seconds.

mosquitto_sub -h broker.connectmq.com -V mqttv5 -i persist-fixed-001 \
  -c -x 600 -q 1 -t "$PREFIX/queue" -v

Robustness Checks (Items 9 to 12)

Payload validation belongs in every consumer. Publish garbage on purpose: invalid JSON, a number as a string, an empty payload, a 10 KB string in a field you expect to be short. The consumer should log and skip, not crash and not write nonsense to the database.

for p in '{"temp":' '{"temp":"hot"}' '' '{"unexpected":true}'; do
  mosquitto_pub -h broker.connectmq.com -i bad-$RANDOM \
    -t "$PREFIX/sensor1/telemetry" -m "$p"
done

Limits are where public and production environments differ most, so test the behavior, not just the number. On the public broker, a payload over 64 KB, more than 10 messages per second from one client, an eleventh subscription or a ninth topic level should all be refused. Make sure your client surfaces that as an error and backs off rather than hammering the broker. On MQTT 5, look for reason codes such as 0x97 (quota exceeded) in the disconnect or acknowledgment. Size limits on a managed MQTT broker depend on the plan; on ConnectMQ, for example, the free Serverless plan allows 256 KB messages and the paid Serverless plan 1 MB.

# 70 KB payload: should be rejected on the public broker
head -c 71680 /dev/urandom | base64 > /tmp/big.txt
mosquitto_pub -h broker.connectmq.com -V mqttv5 -i big-$RANDOM \
  -t "$PREFIX/big" -f /tmp/big.txt -d

For wildcard design, subscribe with the exact filter your service uses and publish to topics that should and should not match. A filter like +/+/telemetry often matches more than intended.

Load and soak testing is the last step. Simulate the real number of devices with realistic intervals and payload sizes, and run it for several hours. Do this against a private broker rather than the public one, since the public per-client rate limit is deliberately low. The MQTT load testing tools roundup compares options.

Quick Reference Table

Check Pass criteria Typical failure
Auth and TLS Clear error on bad credentials or port Silent infinite retry
Client ID One session per device Two devices kicking each other
Reconnect Subscriptions restored automatically Connected but deaf client
Keep alive Dead link detected in about 1.5x interval Device offline for minutes unnoticed
QoS Effective QoS as designed, duplicates harmless Double-counted events
Retained New subscriber sees last value; clearable Stale value forever
Last Will Fires on crash only Fires on clean shutdown or never
Sessions Offline QoS 1 messages delivered Clean session by accident
Payloads Bad input skipped and logged Consumer crash
Limits Predictable rejection and backoff Retry storm
Wildcards Only intended topics matched Data leak across tenants or sites
Load Stable memory and latency for hours Slow leak after day two

Tools That Make Testing Faster

Command-line clients are perfect for scripts, but a visual tool speeds up exploratory testing. ConnectMQ ships an in-browser MQTT client and a live topic explorer in the portal, plus a Desktop MQTT explorer and a VS Code extension, so you can watch topics while your test scripts run. Whatever you use, save your test commands in the repository so the next release runs the same checklist. If you are new to the protocol overall, the MQTT tutorial is the best place to build the foundation these checks rely on.

Test on a Private ConnectMQ Broker

Once the public-broker checks pass, repeat them on the environment you will actually ship to. ConnectMQ gives you a private broker with TLS on 8883, WebSocket on 8084, per-plan quotas and generated credentials in a few clicks. Create a free Serverless broker at app.connectmq.com, no credit card required, swap in YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID, and run the same checklist against it before go-live.

Frequently Asked Questions

What should an MQTT testing checklist include?

At minimum: authentication and TLS, client ID uniqueness, reconnect with resubscribe, keep alive, QoS, retained messages, Last Will, persistent sessions, payload validation, broker limits, wildcard matching and a multi-hour load test.

Can I test my MQTT application on a public broker?

Yes, for functional checks. Use a unique client ID and topic prefix and never send sensitive data, because public topics are readable by anyone. Run load tests on a private broker.

How do I test MQTT Last Will without unplugging hardware?

Kill the client process with a hard kill (for example kill -9) so it cannot send DISCONNECT. The broker publishes the will after the keep alive period expires.

Why does my client stop receiving messages after a reconnect?

It probably subscribes only once at startup. Move the subscribe calls into the on-connect callback, or use a persistent session, so subscriptions are restored on every reconnect.

Related articles

Why Your MQTT Client Keeps Disconnecting (and How to Fix It)

Reconnect loops are one of the most common MQTT problems. Learn the five usual causes, from duplicate client IDs to keep alive timeouts and oversized packets, and follow a diagnosis flow that pinpoints yours.

8 min read

Who Invented MQTT? The History from Oil Pipelines to IoT

MQTT was invented in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom to monitor oil pipelines over satellite. Follow its path to open source, OASIS, ISO and today's managed cloud brokers.

7 min read

MQTT Maximum Message Size: Limits and Large Payload Strategies

MQTT can technically carry a 256 MB packet, but brokers, client libraries and devices impose far lower limits. Learn where the real limits are, what happens when you exceed them, and how to send large payloads.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.