Bir MQTT smoke testi tek bir soruyu bir dakikadan kısa sürede yanıtlar: Bu broker şu anda istemcimi kabul edip bir mesajı teslim ediyor mu? Aşağıda bağlanan, abone olan, benzersiz bir token yayınlayan ve onun geri gelmesini bekleyen üç kopyala-yapıştır betik var: Bash, Python ve Node.js. Her biri adım başına PASS/FAIL yazdırır ve 0 ya da 1 ile çıkar; böylece aynı dosya dizüstü bilgisayarınızda, CI'da ve bir cron job'ında çalışır.
Smoke test neyi kanıtlar?
| Adım | Kanıtladığı | Tipik hata |
|---|---|---|
| Bağlanma | DNS, ağ yolu, port, TLS, kimlik bilgileri | Connection refused, not authorised, TLS hataları |
| Abone olma | Subscribe ACL'leri, topic geçerliliği | All subscription requests were denied. |
| Yayınlama (QoS 1) | Publish ACL'leri, broker'ın mesajı kabul etmesi | PUBACK beklerken zaman aşımı |
| Alma | Yönlendirmenin uçtan uca çalışması | Zaman aşımı içinde hiçbir şey gelmez |
Her çalıştırma testmqtt/smoke/ altında benzersiz bir topic ve rastgele bir token kullanır; böylece paralel çalıştırmalar birbirlerinin mesajlarını görmez ve eski retained veriler yanlış bir başarıya yol açamaz. Tüm betikler aynı ortam değişkenlerini okur:
export MQTT_HOST=broker.connectmq.com # your broker
export MQTT_PORT=8883 # 1883 plain, 8883 TLS
export MQTT_USER=username # optional
export MQTT_PASS=password # optional
export MQTT_TIMEOUT=10 # seconds per stepBash (Mosquitto istemcileri)
Yalnızca mosquitto_pub ve mosquitto_sub gerekir (apt install mosquitto-clients veya brew install mosquitto). Subscriber arka planda çalışır, tam olarak bir mesaj bekler (-C 1) ve zaman aşımından sonra vazgeçer (-W).
#!/usr/bin/env bash
# MQTT smoke test: connect, subscribe, publish, receive. Exit 0 = pass.
set -u
HOST=${MQTT_HOST:-broker.connectmq.com}
PORT=${MQTT_PORT:-1883}
TIMEOUT=${MQTT_TIMEOUT:-10}
TOPIC="testmqtt/smoke/$(hostname -s)-$$-$RANDOM"
TOKEN="smoke-$(date +%s)-$RANDOM"
OUT=$(mktemp); ERR=$(mktemp)
trap 'rm -f "$OUT" "$ERR"' EXIT
ARGS=(-h "$HOST" -p "$PORT" -q 1)
[ -n "${MQTT_USER:-}" ] && ARGS+=(-u "$MQTT_USER" -P "${MQTT_PASS:-}")
[ "$PORT" = 8883 ] && ARGS+=(--capath /etc/ssl/certs)
fail() { echo "FAIL $1"; exit 1; }
command -v mosquitto_sub >/dev/null || fail "mosquitto clients are not installed"
mosquitto_sub "${ARGS[@]}" -t "$TOPIC" -C 1 -W "$TIMEOUT" >"$OUT" 2>"$ERR" &
SUB=$!
sleep 1
kill -0 "$SUB" 2>/dev/null || fail "connect/subscribe: $(cat "$ERR")"
echo "PASS connect + subscribe $HOST:$PORT"
mosquitto_pub "${ARGS[@]}" -t "$TOPIC" -m "$TOKEN" 2>"$ERR" || fail "publish: $(cat "$ERR")"
echo "PASS publish $TOPIC"
wait "$SUB" || fail "receive: nothing within ${TIMEOUT}s $(cat "$ERR")"
grep -qx "$TOKEN" "$OUT" || fail "receive: unexpected payload '$(cat "$OUT")'"
echo "PASS round trip ${SECONDS}s total"Başarılı bir çalıştırma şöyle görünür:
PASS connect + subscribe broker.connectmq.com:8883
PASS publish testmqtt/smoke/build-01-4127-18832
PASS round trip 1s totalBir saniyelik sleep, publish'ten önce aboneliğin kaydedilmesine zaman tanır. Bu sürümdeki tek kestirme budur; Python ve Node betikleri bunun yerine gerçek SUBACK'i bekler. Daha fazla mosquitto_pub/mosquitto_sub seçeneği için mosquitto_pub ve mosquitto_sub örnekleri makalesine bakın.
Python (paho-mqtt 2.x)
pip install "paho-mqtt>=2". Betik her onayı (CONNACK, SUBACK, PUBACK) bekler ve reason code'ları kontrol eder; böylece reddedilen bir giriş veya engellenen bir abonelik, broker'ın kendi mesajıyla doğru adımda başarısız olur.
#!/usr/bin/env python3
"""MQTT smoke test: exit 0 if connect, subscribe, publish and receive all pass."""
import os, sys, threading, time, uuid
import paho.mqtt.client as mqtt
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
TIMEOUT = float(os.getenv("MQTT_TIMEOUT", "10"))
TOPIC = f"testmqtt/smoke/{uuid.uuid4().hex[:12]}"
TOKEN = uuid.uuid4().hex
connected, subscribed, received = threading.Event(), threading.Event(), threading.Event()
state = {}
def step(ok, name, detail=""):
print(f"{'PASS' if ok else 'FAIL'} {name} {detail}".rstrip())
if not ok:
sys.exit(1)
def on_connect(client, userdata, flags, reason_code, properties):
state["connack"] = reason_code
connected.set()
def on_subscribe(client, userdata, mid, reason_code_list, properties):
state["suback"] = reason_code_list[0]
subscribed.set()
def on_message(client, userdata, msg):
if msg.payload.decode(errors="replace") == TOKEN:
received.set()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"smoke-{TOKEN[:8]}")
client.on_connect, client.on_subscribe, client.on_message = on_connect, on_subscribe, on_message
if os.getenv("MQTT_USER"):
client.username_pw_set(os.environ["MQTT_USER"], os.getenv("MQTT_PASS", ""))
if PORT == 8883:
client.tls_set()
start = time.perf_counter()
try:
client.connect(HOST, PORT, keepalive=30)
except Exception as exc:
step(False, "connect", f"{HOST}:{PORT} {exc}")
client.loop_start()
ok = connected.wait(TIMEOUT) and not state["connack"].is_failure
step(ok, "connect", f"{HOST}:{PORT} {state.get('connack', 'timeout')}")
client.subscribe(TOPIC, qos=1)
ok = subscribed.wait(TIMEOUT) and not state["suback"].is_failure
step(ok, "subscribe", str(state.get("suback", "timeout")))
info = client.publish(TOPIC, TOKEN, qos=1)
info.wait_for_publish(TIMEOUT)
step(info.is_published(), "publish", TOPIC)
step(received.wait(TIMEOUT), "round trip", f"{(time.perf_counter() - start) * 1000:.0f} ms")
client.disconnect()
client.loop_stop()Ağ döngüsü bir daemon thread'inde çalıştığı için sys.exit(1), ilk başarısız adımda süreci hemen sonlandırır. 2.x callback imzalarını hatırlamanız mı gerekiyor? paho-mqtt 2 eğitimine göz atın.
Node.js (MQTT.js 5)
npm install mqtt. MQTT.js 5, betiği doğrusal tutan promise tabanlı metotlarla gelir (connectAsync, subscribeAsync, publishAsync, endAsync). URL aldığı için aynı dosya WebSockets'i de test eder: MQTT_URL=wss://broker.connectmq.com:8084/mqtt.
// MQTT smoke test: node smoke.mjs — exit code 0 = pass
import mqtt from 'mqtt';
import { randomUUID } from 'node:crypto';
const host = process.env.MQTT_HOST ?? 'broker.connectmq.com';
const port = process.env.MQTT_PORT ?? '1883';
const url = process.env.MQTT_URL ?? `${port === '8883' ? 'mqtts' : 'mqtt'}://${host}:${port}`;
const timeout = Number(process.env.MQTT_TIMEOUT ?? 10) * 1000;
const topic = `testmqtt/smoke/${randomUUID().slice(0, 12)}`;
const token = randomUUID();
const step = (ok, name, detail = '') => {
console.log(`${ok ? 'PASS' : 'FAIL'} ${name} ${detail}`.trimEnd());
if (!ok) process.exit(1);
};
const within = (promise, what) =>
Promise.race([promise, new Promise((_, reject) =>
setTimeout(() => reject(new Error(`${what} timed out after ${timeout} ms`)), timeout))]);
const start = performance.now();
let client;
try {
client = await within(mqtt.connectAsync(url, {
clientId: `smoke-${token.slice(0, 8)}`,
username: process.env.MQTT_USER,
password: process.env.MQTT_PASS,
reconnectPeriod: 0,
connectTimeout: timeout,
}, false), 'connect');
step(true, 'connect', url);
} catch (err) {
step(false, 'connect', `${url} ${err.message}`);
}
const received = new Promise((resolve) => {
client.on('message', (t, payload) => {
if (t === topic && payload.toString() === token) resolve();
});
});
try {
const [grant] = await within(client.subscribeAsync(topic, { qos: 1 }), 'subscribe');
step(grant.qos < 128, 'subscribe', `granted QoS ${grant.qos}`);
await within(client.publishAsync(topic, token, { qos: 1 }), 'publish');
step(true, 'publish', topic);
await within(received, 'receive');
step(true, 'round trip', `${Math.round(performance.now() - start)} ms`);
} catch (err) {
step(false, 'step', err.message);
}
await client.endAsync();128 (0x80) değerinde verilen bir QoS, broker'ın aboneliği reddettiği anlamına gelir; genellikle bir ACL yüzünden.
CI'da ve zamanlanmış olarak çalıştırın
Betikler hata durumunda sıfırdan farklı bir kodla çıktığı için doğrudan her pipeline'a eklenebilir. Bir GitHub Actions adımı:
- name: MQTT smoke test
env:
MQTT_HOST: broker.connectmq.com
MQTT_PORT: "8883"
MQTT_USER: ${{ secrets.MQTT_USER }}
MQTT_PASS: ${{ secrets.MQTT_PASS }}
run: |
sudo apt-get update && sudo apt-get install -y mosquitto-clients
bash smoke.shBroker'a, sertifikalarına veya ACL'lerine dokunan her deploy'dan sonra ve sentetik bir kontrol olarak zamanlanmış şekilde (örneğin beş dakikada bir) çalıştırın. GitHub Actions'ta MQTT testleri, service container olarak broker içeren eksiksiz bir workflow gösterir.
Bir hatayı okumak
| Başarısız adım | İlk bakılacak yer |
|---|---|
connect, Connection refused / zaman aşımı | Host, port, firewall; listener çalışıyor mu? |
connect, not authorised / bad user name or password | Kimlik bilgileri ya da anonim erişimin kapalı olması |
| connect, TLS / sertifika hatası | 1883 yerine 8883 portu, CA paketi, sertifikadaki hostname |
| subscribe reddedildi, verilen QoS 128 | Test kullanıcısının subscribe ACL'i |
| publish zaman aşımına uğruyor | Publish ACL'i (MQTT 5 0x87 döndürür), aşırı yüklü broker |
| round trip zaman aşımına uğruyor, önceki adımlar geçiyor | Uyuşmayan topic, mesajı sessizce atan publish ACL'i (MQTT 3.1.1), bridge veya cluster yönlendirmesi |
Tüm bağlantı hataları ve çözümleri MQTT bağlantı hataları makalesinde ele alınır; reason code'ları MQTT reason code aracı ile çözebilirsiniz. Kontrolü birinin elle tekrarlamasını istiyorsanız online MQTT istemcisi aynı bağlan–abone ol–yayınla döngüsünü tarayıcıda gerçekleştirir.
Smoke test mesajlarını canlı izleyin
Smoke test, yazabileceğiniz en ucuz MQTT testidir ve en sık çalıştıracağınız test de odur. Kısa tutun, her çalıştırmayı benzersiz yapın, bozulan adımı belirterek net biçimde başarısız olun ve CI'a ve bir zamanlamaya bağlayın; MQTT broker test kontrol listesindeki daha derin kontroller bunun üzerine kurulabilir.
Sıkça sorulan sorular
Bir MQTT smoke testi neyi kontrol etmeli?
En azından tam bir gidiş-dönüş: gerçek kimlik bilgileriyle bağlanmak, abone olmak, benzersiz bir token yayınlamak ve tam olarak o token'ı bir zaman aşımı içinde geri almak. Bu tek yol DNS'i, ağı, TLS'i, kimlik doğrulamayı, ACL'leri ve mesaj yönlendirmesini sınar.
Smoke test ile health check arasındaki fark nedir?
Health check genellikle broker'ın ayakta olduğunu, çoğu zaman bağlanarak ya da bir metrik okuyarak doğrular. Smoke test ise uçtan uca gerçek bir mesaj göndererek broker'ın işini yaptığını kanıtlar. Birçok ekip smoke testi health check olarak kullanır.
Smoke testim neden yalnızca bazen başarısız oluyor?
En yaygın neden, abonelik onaylanmadan publish yapılması ve bu yüzden ilk mesajın kaçırılmasıdır. Publish'ten önce SUBACK'i bekleyin, QoS 1 ve her çalıştırma için benzersiz bir topic kullanın, alma adımına da gerçekçi bir zaman aşımı verin.



