Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mqtt.connect giving error in Jest: Jest has detected the following 1 open handle potentially keeping Jest from exiting #1482

Closed
anthonyma94 opened this issue May 13, 2022 · 7 comments

Comments

@anthonyma94
Copy link

I have a test for a class that revolves around MQTT:

MQTTHMIPlugin.ts

export class MQTTHMIPlugin {
    private clients: MqttClient[];

    // Specifically not using a constructor for DI reasons
    init() {
        if (!process.env.MQTT_URL) {
            throw new Error("Missing MQTT_URL environment variable.");
        }
        const urls = process.env.MQTT_URL.split(/,\s*/);

        this.clients = [];

        for (const url of urls) {
            const client = mqtt.connect(url);
            client.on("connect", () => {
                logger.info(`MQTT client connected to ${url}`);
            });
            this.clients.push(client);
        }
    }
    cleanup() {
        for (const client of this.clients) {
            if (client.connected) {
                client.end();
            }
        }
    }
   ...
}

MQTTHMIPlugin.spec.ts

describe("MQTT HMI Plugin tests", () => {
    let plugin: MQTTHMIPlugin;
    let aedes: ReturnType<typeof Aedes>;  // in-memory MQTT server
    let server: net.Server;  // in-memory MQTT server

    beforeAll(() => {
        process.env.MQTT_URL = "mqtt://localhost:1883";
        aedes = Aedes();
        server = net.createServer(aedes.handle);
        server.listen(1883, function () {
            // console.log(`MQTT server started on port 1883.`);
        });
    });

    beforeEach(() => {
        if (plugin) {
            plugin.cleanup();
        }
        plugin = new MQTTHMIPlugin();
    });

    afterAll((done) => {
        plugin.cleanup();
        aedes.close(() => {
            server.close();
        });
        done();
    });

    test("init function", (done) => {
        plugin.init();
        console.log(aedes.connectedClients);
        expect(0).toBe(0);
        plugin.cleanup();
        done();
    });
});

Even though I'm calling cleanup() in 3 different places and calling done, Jest still thinks there's an open handle and refuses to exit:

Jest has detected the following 1 open handle potentially keeping Jest from exiting:

  ●  Timeout

      23 |
      24 |         for (const url of urls) {
    > 25 |             const client = mqtt.connect(url);
         |                                 ^
      26 |             client.on("connect", () => {
      27 |                 logger.info(`MQTT client connected to ${url}`);
      28 |             });

How can I have Jest exit properly, without using --forceExit?

@FreTimmerman
Copy link

FreTimmerman commented Oct 11, 2022

i had the same problem. this issue got me thinking and i tried something.

i got a successful close of the mqtt client by adding a waiting period (mine was 3 seconds)

export const mqttShutdown = () => {
  return new Promise<void>((resolve) =>
    client.end(true, {}, () => {
      setTimeout(() => {
        resolve();
      }, 3000);
    }),
  );
};

so i guess in your case that will become: (below code is untested, just quickly written to try to translate my approach to your callback-based and multi-client approach)

cleanup(doneCallback) {
  Promise.all(
    this.clients.map((client) => {
      return new Promise((resolve) => {
        if (client.connected) {
          client.end(true, {}, () => {
            setTimeout(resolve, 3000);
          });
        } else {
          resolve();
        }
      });
    });
  )
  .then(() => {
    doneCallback();
  });
}

and then use

plugin.cleanup(done)

do note that i added true to the client.end(), in order to force it closed. i found that this speeds up the closing.

and since by default you can't have more than 5s for a hook in jest this is useful...

@sibelius
Copy link

is this for integration tests?

@sibelius
Copy link

can you provide a sample test with jest?

@robertsLando
Copy link
Member

Anyone that would like to try mqtt@beta to see if this error is gone now?

@sibelius
Copy link

does it make sense to expose mqttserver for testing purposes? and add some guides of how to test with jest?

https://github.com/mqttjs/MQTT.js/blob/main/test/server_helpers_for_client_tests.js
https://github.com/mqttjs/MQTT.js/blob/main/test/server.js

@robertsLando
Copy link
Member

Follow: #1641

@robertsLando
Copy link
Member

MQTT 5.0.0 BETA is now available! Try it out and give us feedback: npm i mqtt@beta. It may fix your issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants