Stubbing knihovny MQTT.js v testech Ember.js pomocí Sinon.JS

Využil jsem příležitosti upgradovat podporu ember-mqttjs na Ember v4 pro refaktorování testů doplňků. Během minulého EmberFestu (který se konal v Římě 30. září a 1. října 2021) jsem si vyslechl spoustu skvělých přednášek. Jeden z nich upoutal mou pozornost, protože se zabýval tématem testování a zejména maketami a útržky.

Gonçalo Morais se během své přednášky „Mock &Roll“ ponořil do tohoto argumentu několika ohromujícími radami, které mi otevřely mysl a umožnily mi přemýšlet o tom, jak zlepšit své testy mqtt ember addonů.

Po několika experimentech s mock-socket knihovnou jsem si uvědomil, že tento nástroj již není využíván, a zkombinováním některých vyhledávání v dokumentaci Sinon.JS jsem našel způsob, jak přerušit závislost MQTT.js předstíráním metody připojení.

Tato metoda vrací klienta a spouští některé události, které jsou užitečné pro pochopení stavu připojení mqtt. Spojením metod nahrazování a falešných metod ze Sinon.JS jsem našel způsob, jak simulovat chování metody připojení MQTT.js, která vrací klientovi potřebné metody a spouští potřebné události.

Jak můžete vidět dále na ukázce kódu připojené k tomuto příspěvku, musím k vtipu přidat nějaké triky, protože například connect událost musí být spuštěna po prodlevě, zatímco falešný klient deklaruje obsluhu události, která musí být přítomna, když je událost spuštěna. Nebo například metody odběru a publikování potřebují zavolat zpětné volání se správnými parametry.

Jakmile jsem objevil tyto triky, testy běží správně a mohu předpokládat, že můj kód funguje dobře, spoléhám na to, že klient MQTT.js bude řádně otestován.

Ukázka kódu

Toto je příklad testu připojení, ale celý kód najdete v úložišti doplňků:

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import sinon from 'sinon';
import mqttjs from 'mqtt/dist/mqtt';
import Service from '@ember/service';
import Evented from '@ember/object/evented';
import { later } from '@ember/runloop';

class MqttServiceStub extends Service.extend(Evented) {}

module('Unit | Service | mqtt', function (hooks) {
  let mqttHost = 'ws://localhost:8883';
  let mqttTopic = 'presence';
  let mqttMessage = 'Hello';

  let mqttServiceStub;

  setupTest(hooks);

  hooks.afterEach(() => {
    sinon.restore();
  });

  // ...

  //Testing mqtt connect
  test('mqtt connect success', async function (assert) {
    let service = this.owner.lookup('service:mqtt');
    let done = assert.async();
    assert.expect(3);
    mqttServiceStub = new MqttServiceStub();
    sinon.replace(
      mqttjs,
      'connect',
      sinon.fake(() => {
        later(() => {
          mqttServiceStub.trigger('connect');
        }, 100);
        return {
          on: (sEvent) => {
            mqttServiceStub.on(sEvent, () => {
              if (sEvent === 'connect') {
                return service.onConnect();
              }
            });
          },
        };
      })
    );
    try {
      service.on('mqtt-connected', () => {
        assert.ok(true);
      });
      await service.connect(mqttHost);
      assert.ok(service);
      assert.ok(service.isConnected);
    } catch {
      assert.ok(false);
      assert.ok(false);
      assert.ok(false);
    } finally {
      done();
    }
  });

Pokud si všimnete nějakých chyb nebo potřebujete více informací o kódu, budu rád, když se ozvete. Kontaktujte mě prostřednictvím mé kontaktní stránky.