GitHub Actions'ta MQTT test etmek için job içinde bir broker başlatın, bağlantı kabul edene kadar bekleyin ve test paketinizi ona karşı çalıştırın; ardından aynı testleri, kimlik bilgileri depo secret'ı olarak saklanan barındırılan bir broker'a karşı çalıştıran ikinci bir job ekleyin. İlk job her commit'te mantık hatalarını yakalar; ikincisi TLS, kimlik doğrulama ve ACL sorunlarını cihazlarınızdan önce yakalar.
Bu rehber her iki durum için kopyala-yapıştır workflow'lar sunuyor ve ilk denemelerin çoğunu bozan Mosquitto varsayılanını açıklıyor.
"Local only mode" tuzağı
İlk akla gelen bir service container'dır:
services:
mosquitto:
image: eclipse-mosquitto:2
ports:
- 1883:1883Container başlar, port eşlenir ve tüm testler Connection refused veya ECONNRESET ile başarısız olur. Broker logu nedenini açıklar:
mosquitto version 2.0.x starting
Config loaded from /mosquitto/config/mosquitto.conf.
Starting in local only mode. Connections will only be possible from clients running on this machine.
Create a configuration file which defines a listener to allow remote access.Sürüm 2.0'dan beri, yapılandırmasında listener olmayan Mosquitto yalnızca kendi container'ının içindeki loopback arayüzüne bağlanır. Port eşlemesi üzerinden gelen trafik dışarıdan geldiği için reddedilir. Image'ın varsayılan yapılandırma dosyasında tüm seçenekler yorum satırıdır; dolayısıyla elde ettiğiniz tam olarak budur.
Service container'lar çözümü zahmetli hale getirir: herhangi bir step çalışmadan önce başlarlar, yani deponuz (ve içindeki mosquitto.conf) henüz checkout edilmemiştir. En basit ve öngörülebilir yaklaşım, broker'ı bir step içinde kendiniz başlatmaktır. Aynı sorun yerel kurulumlar için Docker'da Mosquitto ve Mosquitto yapılandırması yüklenmiyor yazılarında ele alınıyor.
Job 1: Workflow içinde Mosquitto
Depoya minimal bir test yapılandırması commit edin:
listener 1883 0.0.0.0
allow_anonymous true
persistence false
log_dest stdoutRetleri test etmek için parola dosyası veya ACL içeren daha sıkı varyantları Mosquitto config oluşturucu ile üretebilirsiniz. Ardından broker'ı checkout'tan sonra başlatın ve yalnızca TCP değil, gerçekten MQTT üzerinden yanıt verene kadar bekleyin:
name: mqtt-tests
on:
push:
pull_request:
jobs:
local-broker:
runs-on: ubuntu-latest
env:
MQTT_HOST: localhost
MQTT_PORT: "1883"
steps:
- uses: actions/checkout@v4
- name: Start Mosquitto
run: |
docker run -d --name mosquitto -p 1883:1883 \
-v "$PWD/test/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro" \
eclipse-mosquitto:2
for i in $(seq 1 30); do
if docker exec mosquitto mosquitto_pub -h localhost -t testmqtt/ci/ready -m ok; then
echo "broker ready"; exit 0
fi
sleep 1
done
echo "broker did not start"; docker logs mosquitto; exit 1
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements-dev.txt
- run: pytest -v tests/
- name: Broker logs
if: failure()
run: docker logs mosquittoDöngünün portu kontrol etmek yerine neden mosquitto_pub çalıştırdığı: Docker'ın port proxy'si, container var olduğu anda — broker içeride dinlemeye başlamadan bile — host üzerinde TCP bağlantılarını kabul eder. Başarılı bir publish, broker'ın ayakta olduğunu kanıtlar. eclipse-mosquitto image'ı komut satırı istemcileriyle gelir; ek bir şey kurmanız gerekmez.
Bir Node.js projesinde Python step'lerini actions/setup-node@v4, npm ci ve npm test ile değiştirin; broker step'leri aynı kalır.
Job 2: Barındırılan bir broker'a karşı testler
CI'daki bir container anonimdir, şifresizdir ve her çalıştırmada sıfırdan yapılandırılır; production'ın tam tersi. İkinci bir job aynı test paketini gerçek kimlik bilgileriyle TLS üzerinden çalıştırır. Bunları Settings → Secrets and variables → Actions altında MQTT_USER ve MQTT_PASS olarak saklayın:
hosted-broker:
runs-on: ubuntu-latest
needs: local-broker
# secrets are not available to pull requests from forks
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
concurrency:
group: mqtt-hosted-${{ github.ref }}
cancel-in-progress: true
env:
MQTT_HOST: broker.connectmq.com
MQTT_PORT: "8883"
MQTT_USER: ${{ secrets.MQTT_USER }}
MQTT_PASS: ${{ secrets.MQTT_PASS }}
MQTT_TOPIC_PREFIX: testmqtt/ci/${{ github.run_id }}-${{ github.run_attempt }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements-dev.txt
- run: pytest -v tests/Her parçanın görevi:
needs: local-broker, ağ kullanan daha yavaş job'ı yalnızca hızlı job geçtiğinde çalıştırır.if:koşulu, secret almayan fork pull request'lerinde job'ı atlar. Bu koşul olmadan job, gerçek bir hata gibi görünen bir kimlik doğrulama hatasıyla başarısız olur.MQTT_TOPIC_PREFIXçalıştırma ID'sini ve deneme numarasını içerir; böylece yeniden çalıştırmalar ve paralel workflow'lar asla topic paylaşmaz. Testleriniz her topic'i bundan oluşturmalıdır; fixture kalıbı için MQTT entegrasyon testi yazısına bakın.- GitHub secret değerlerini loglarda otomatik olarak maskeler, ancak yine de bağlantı seçeneklerini debug çıktısına yazdırmaktan kaçının.
Barındırılan job'ı hızlı ve düzenli tutmak
Test paketi büyüdükçe ağ kullanan job'ı güvenilir tutan birkaç alışkanlık:
- Bir alt küme çalıştırın. TLS, kimlik bilgisi veya ACL gerektiren testleri işaretleyin (örneğin
@pytest.mark.hostedveya ayrı bir Jest projesi) ve barındırılan job'da yalnızca bunları çalıştırın. Saf mantık zaten yerel job tarafından kapsanıyor. - Retained mesajları temizleyin. Bir testin oluşturduğu her retained mesaj çalıştırmadan sonra da kalır. Teardown'da boş bir retained publish ile temizleyin ya da retained davranışı test etmeyen testlerde retain bayrağından kaçının.
- Temiz oturum kullanın. Rastgele client ID'li kalıcı oturumlar süreleri dolana kadar broker'da birikir. Test oturumlarla ilgili değilse temiz oturum (veya MQTT 5'te kısa bir session expiry) kullanın.
- Timeout'ları ağa göre ayarlayın. Yerelde 20 ms'de gelen bir mesaj, internet üzerinden paylaşımlı bir runner'dan bir saniye sürebilir. Bu job'da 5–10 saniyelik mesaj timeout'ları kullanın; testler yine de mesaj gelir gelmez biter.
Matris ile birden fazla broker'a karşı test
Kütüphanenizin veya gateway'inizin birden fazla broker ile çalışması gerekiyorsa, bir matris YAML'ı kopyalamadan test paketini her birine karşı çalıştırır:
compat:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image: ["eclipse-mosquitto:2", "emqx/emqx:latest", "hivemq/hivemq-ce:latest"]
steps:
- uses: actions/checkout@v4
- name: Start ${{ matrix.image }}
run: docker run -d --name broker -p 1883:1883 ${{ matrix.image }} ${{ startsWith(matrix.image, 'eclipse-mosquitto') && 'mosquitto -c /mosquitto-no-auth.conf' || '' }}
- name: Wait for MQTT
run: |
pip install paho-mqtt
python scripts/wait_for_broker.py localhost 1883 --timeout 60
- run: pip install -r requirements-dev.txt && pytest -v tests/EMQX ve HiveMQ CE, Mosquitto'dan daha geç başlar ve image'larında mosquitto_pub bulunmaz; bu yüzden hazır olma kontrolü runner üzerinden çalışır. Yeni bir broker sürümünün build'inizi habersizce bozmaması için gerçek pipeline'larda kesin image tag'leri sabitleyin. Küçük bir wait_for_broker.py'nin tek yapması gereken, başarılı olana veya timeout dolana kadar client.connect()'i tekrarlamaktır.
Başarısız CI çalıştırmalarında hata ayıklama
| Job logundaki hata | Neden | Çözüm |
|---|---|---|
Başlatmadan hemen sonra Connection refused / ECONNREFUSED | Broker henüz hazır değil veya local only mode | MQTT hazır olma döngüsünü kullanın; bir listener tanımlayın |
Her bağlantıda ECONNRESET | Port eşlenmiş ama Mosquitto loopback'e bağlı | Yapılandırmada listener 1883 0.0.0.0 |
Barındırılan job'da Connection Refused: not authorised | Secret eksik (fork PR) veya yanlış kullanıcı | if: koşulunu ve secret adlarını kontrol edin |
[SSL: CERTIFICATE_VERIFY_FAILED] | TLS portu CA olmadan kullanılmış veya yanlış hostname | Sistem CA paketini ve broker'ın gerçek hostname'ini kullanın |
| Testler tek başına geçiyor, matriste başarısız | Job'lar arasında paylaşılan topic'ler | Çalıştırma ID'si ve matris değeri içeren topic öneki |
docker logs çıktısını basan if: failure() step'ini kalıcı olarak tutmaya değer: broker logu reddedilen bağlantıları, ACL retlerini ve client ID'leri gösterir. İstemci hatalarındaki reason code'lar için MQTT reason code arama aracını kullanın.
Pipeline'ı doğrulamak
Pipeline'ın başarısız olabildiğini kanıtlamasını sağlayın. Yanlış topic'e publish eden bir branch push edin ve job'ın kırmızıya döndüğünü doğrulayın. Ardından barındırılan job çalışırken aynı kimlik bilgileriyle online MQTT istemcisinde testmqtt/ci/# topic'ine abone olun: çalıştırmaya özel önekin belirdiğini ve test trafiğinin aktığını göreceksiniz; bu da secret'ların, TLS'in ve ACL'lerin amaçlandığı gibi çalıştığını doğrular.
CI test trafiğini canlı izleyin
Güvenilir bir MQTT pipeline'ı iki şeye ihtiyaç duyar: job içinde bağlantı kabul edecek şekilde yapılandırılmış ve gerçek bir publish ile kontrol edilmiş bir broker, ve production'da kullandığınız TLS'i, kimlik bilgilerini ve ACL'leri çalıştıran ikinci bir aşama. Benzersiz topic önekleri ve fork build'lerinden uzak tutulan secret'larla ikisi de her push'ta birbirini etkilemeden çalışır. Container'ları doğrudan test kodundan yönetmeyi tercih ediyorsanız Testcontainers ile MQTT testleri yazısına bakın.
Sıkça sorulan sorular
GitHub Actions job'ım neden Mosquitto container'ına bağlanamıyor?
Mosquitto 2.x, yapılandırılmış bir listener olmadığında local only mode'da başlar ve yalnızca kendi container'ının içinden gelen bağlantıları kabul eder. Onu listener 1883 ve allow_anonymous true (veya gerçek kimlik bilgileri) tanımlayan bir yapılandırmayla başlatın; örneğin image ile gelen /mosquitto-no-auth.conf dosyasıyla.
GitHub secret'ları fork'lardan gelen pull request'lerde kullanılabilir mi?
Hayır. Fork'lanmış depolardan gelen pull request'lerin tetiklediği workflow'lar depo secret'larını almaz. Barındırılan broker testlerini yalnızca push'larda ve aynı depodan gelen pull request'lerde çalıştırın; geri kalan her şey için yerel broker job'ını kullanın.
CI'da yerel Mosquitto mu yoksa barındırılan bir broker mı kullanılmalı?
İkisini de. Yerel bir container her commit'te hızlı ve izole geri bildirim verir. Barındırılan bir broker'a karşı çalışan bir job ise TLS, kimlik doğrulama ve ACL'leri production'daki yapılandırmalarıyla doğrular; atılabilir bir container bunu yapamaz.



