最新消息: 电脑我帮您提供丰富的电脑知识,编程学习,软件下载,win7系统下载。

如何调试“ Jest已检测到以下……打开的句柄,可能会阻止Jest退出”

IT培训 admin 14浏览 0评论

如何调试“ Jest已检测到以下……打开的句柄,可能会阻止Jest退出”

我在Jest中的异步代码还有其他问题。我的previous question(用于同一项目)与在Jest引导程序中运行异步代码有关。我的新问题与在测试中运行异步数据库调用有关。我的目的是连接到数据库服务并进行调用以确保它们正确读取和写入数据库。我将测试运行在一个Docker容器中,并连接到另一个容器中的MySQL实例。

我正在使用mysql2/promise节点库,这同样表明,将基于回调的数据库操作包装在Promise中。除了关闭连接(以及其他一些连接)外,大多数操作都是异步的。确实,我想知道这是否相关。

我应该以一些代码开头。这是我的测试:

import TestDatabase from '../TestDatabase';
var config = require('../../config/config.json');
import FetchDonations from "../../src/services/FetchDonations";
const envName = 'test';

let database = new TestDatabase(config);

// Connect before all tests
beforeAll(() => {
  console.log('Connect Jest database');
  return database.connect(envName);
});

// Disconnect after all tests
afterAll(async done => {
  console.log('Disconnect Jest database');
  database.close();
  done();
});

describe('Database tests', () => {

  // Before every test
  beforeEach(() => database.beforeEachTest(envName));

  test('Describe this demo test', () => {
    console.log('Test #1');
    expect(true).toEqual(true);
  });

  test('Describe this demo test 2', () => {
    console.log('Test #2');
    expect(true).toEqual(true);
  });

});

这只运行了几个虚拟测试。他们什么也没做,我只是想让之前/之后的钩子起作用。这些是他们应该做的:

  • beforeAll-一次连接到数据库(异步操作)
  • afterAll-一次与数据库断开连接(mysql2中的同步op)
  • beforeEach-在每次测试前运行database.beforeEachTest(),这会截断数据库中的表(异步操作)

这里是TestDatabase的样子-这些是我为帮助数据库测试而编写的实用程序方法:

const mysql = require('mysql2/promise');

export default class TestDatabase {

  constructor(config) {
    this.config = config;
  }

  beforeEachTest(environmentName) {
    console.log('Before a test');

    return this.setForeignKeyChecks(false).then(() => {
      return this.truncateTables();
    }).then(() => {
      return this.setForeignKeyChecks(true);
    }).catch((error) => {
      console.log('Failed to clear down database: ' + error);
    });
  }

  connect(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return mysql.createConnection({
      host: config.host, user: config.username,
      password: config.password
    }).then((connection) => {
      this.connection = connection;
      return this.useDatabase(environmentName);
    }).catch((error) => {
      console.log('Failed to connect to the db');
    });
  }

  getConnection() {
    if (!this.connection) {
      throw 'Database not connected';
    }

    return this.connection;
  }

  dropDatabase(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return this.getConnection().query(
      `DROP DATABASE IF EXISTS ${config.database}`
    );
  }


  createDatabase(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return this.getConnection().query(
      `CREATE DATABASE IF NOT EXISTS ${config.database}`
    );
  }

  useDatabase(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return this.getConnection().query(
      `USE ${config.database}`
    );
  }

  setForeignKeyChecks(value) {
    // Make injected value safe
    var boolStr = value ? '1' : '0';

    return this.getConnection().query(
      `SET FOREIGN_KEY_CHECKS = ${boolStr}`
    );
  }

  getTables() {
    return ['contribution', 'donation', 'expenditure',
      'tag', 'expenditure_tag'];
  }

  truncateTables() {
    return Promise.all(
      this.getTables().map(table => this.truncateTable(table))
    );
  }

  truncateTable(table) {
    return this.getConnection().query(
      `TRUNCATE TABLE ${table}`
    );
  }

  /**
   * Close is synchronous so there is no returned promise
   */
  close() {
    this.getConnection().close();
  }

  getEnvConfig(environmentName) {
    if (!environmentName) {
      throw 'Please supply an environment name'
    }
    if (!this.config[environmentName]) {
      throw 'Cannot find database environment data'
    }

    return this.config[environmentName];
  }
}

现在,如果我运行测试,它们将通过并结束,但是有两个奇怪之处。首先,一些异步console.log输出将在测试摘要后输出,因此我认为我不是按照Jest希望的方式来处理异步。换句话说,我认为摘要应在所有这些之后呈现:

/project/node_modules/.bin/jest tests
  console.log
    Connect Jest database

      at Object.<anonymous> (tests/database/TestDemo.test.js:29:11)

  console.log
    Before a test

      at TestDatabase.beforeEachTest (tests/TestDatabase.js:10:13)

 PASS  tests/database/TestDemo.test.js
  Database tests
    ✓ Describe this demo test (72ms)
    ✓ Describe this demo test 2 (58ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.118s, estimated 3s
Ran all test suites matching /tests/i.
  console.log
    Test #1

      at Object.<anonymous> (tests/database/TestDemo.test.js:46:13)

  console.log
    Before a test

      at TestDatabase.beforeEachTest (tests/TestDatabase.js:10:13)

  console.log
    Test #2

      at Object.<anonymous> (tests/database/TestDemo.test.js:51:13)

  console.log
    Disconnect Jest database

      at _callee$ (tests/database/TestDemo.test.js:35:11)

您可以看到,两个测试的输出都显示在摘要之后,但是第一个测试的beforeEach的输出出现在测试摘要之前。

此外,如果我添加了使用该数据库的真实测试,则会收到错误消息,提示我有未处理的承诺,并且应该尝试Jest的未处理的承诺检测器(--detectOpenHandles)。而且,在这种情况下,Jest会循环停止并需要^ C来返回控制台提示。

因此,我正在尝试使用当前代码进行--detectOpenHandles,尽管我没有得到Jest冻结,但得到了以下内容。

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

  ●  TCPWRAP

      22 |     const config = this.getEnvConfig(environmentName);
      23 | 
    > 24 |     return mysql.createConnection({
         |                  ^
      25 |       host: config.host, user: config.username,
      26 |       password: config.password
      27 |     }).then((connection) => {

      at new Connection (node_modules/mysql2/lib/connection.js:35:27)
      at Object.<anonymous>.exports.createConnection (node_modules/mysql2/index.js:10:10)
      at Object.createConnection (node_modules/mysql2/promise.js:230:31)
      at TestDatabase.connect (tests/TestDatabase.js:24:18)
      at Object.<anonymous> (tests/database/TestDemo.test.js:30:19)

我的观点是,这与我通过更多测试获得的冻结直接相关,在尝试添加更多测试之前,我应该解决此问题。

我已经通过几个调查循环来确定可能导致这种情况的原因,并且代码已被多次调整:

  • [afterAllbeforeEach是异步操作,因此需要将它们return分配给Jest,因此Jest知道等待它们解决。
  • [afterAll会关闭数据库,但这不是异步的,因此我在这里使用Jest的done(),尽管如果没有done()也无法使用它。
  • [TestDatabase包含两个主要方法,beforeEachTestconnect,并且我非常小心以确保它们返回Promises。
  • 我更倾向于束缚物资,而不是异步等待。但是,我在几个方面尝试了async-await,但没有帮助。
  • dropDatabasecreateDatabasesetForeignKeyCheckstruncateTablestruncateTable之类的实用代码均返回承诺。
  • 我已经阅读了Jest async docs,并且有很多方法。主要要点是,如果您正在测试异步内容,则应将Promise返回给Jest,以便进行适当的等待。我真正的测试是同步的,这只是我之前的异步钩子。考虑一下,我想知道这是否是问题吗?

我对Jest相当陌生,也不太熟悉JS异步。每当我认为我对异步有了更好的理解时,就会得到新鲜的弯球。但是,我想知道这是否更多是开玩笑的怪癖,而不是难以理解原始异步。

回答如下:

目前,我已删除--detectOpenHandles并添加了--forceExit。顾名思义,它确保Jest在测试后退出,即使它认为存在未处理的承诺。

对我来说,该选项的存在很有趣-我想知道这意味着误报很普遍。无论哪种方式,我的测试确实确实通过了并且失败了,所以我将把这个问题放在后面的问题上。仍然非常欢迎获得非解决方法的令人满意的答案。

如何调试“ Jest已检测到以下……打开的句柄,可能会阻止Jest退出”

我在Jest中的异步代码还有其他问题。我的previous question(用于同一项目)与在Jest引导程序中运行异步代码有关。我的新问题与在测试中运行异步数据库调用有关。我的目的是连接到数据库服务并进行调用以确保它们正确读取和写入数据库。我将测试运行在一个Docker容器中,并连接到另一个容器中的MySQL实例。

我正在使用mysql2/promise节点库,这同样表明,将基于回调的数据库操作包装在Promise中。除了关闭连接(以及其他一些连接)外,大多数操作都是异步的。确实,我想知道这是否相关。

我应该以一些代码开头。这是我的测试:

import TestDatabase from '../TestDatabase';
var config = require('../../config/config.json');
import FetchDonations from "../../src/services/FetchDonations";
const envName = 'test';

let database = new TestDatabase(config);

// Connect before all tests
beforeAll(() => {
  console.log('Connect Jest database');
  return database.connect(envName);
});

// Disconnect after all tests
afterAll(async done => {
  console.log('Disconnect Jest database');
  database.close();
  done();
});

describe('Database tests', () => {

  // Before every test
  beforeEach(() => database.beforeEachTest(envName));

  test('Describe this demo test', () => {
    console.log('Test #1');
    expect(true).toEqual(true);
  });

  test('Describe this demo test 2', () => {
    console.log('Test #2');
    expect(true).toEqual(true);
  });

});

这只运行了几个虚拟测试。他们什么也没做,我只是想让之前/之后的钩子起作用。这些是他们应该做的:

  • beforeAll-一次连接到数据库(异步操作)
  • afterAll-一次与数据库断开连接(mysql2中的同步op)
  • beforeEach-在每次测试前运行database.beforeEachTest(),这会截断数据库中的表(异步操作)

这里是TestDatabase的样子-这些是我为帮助数据库测试而编写的实用程序方法:

const mysql = require('mysql2/promise');

export default class TestDatabase {

  constructor(config) {
    this.config = config;
  }

  beforeEachTest(environmentName) {
    console.log('Before a test');

    return this.setForeignKeyChecks(false).then(() => {
      return this.truncateTables();
    }).then(() => {
      return this.setForeignKeyChecks(true);
    }).catch((error) => {
      console.log('Failed to clear down database: ' + error);
    });
  }

  connect(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return mysql.createConnection({
      host: config.host, user: config.username,
      password: config.password
    }).then((connection) => {
      this.connection = connection;
      return this.useDatabase(environmentName);
    }).catch((error) => {
      console.log('Failed to connect to the db');
    });
  }

  getConnection() {
    if (!this.connection) {
      throw 'Database not connected';
    }

    return this.connection;
  }

  dropDatabase(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return this.getConnection().query(
      `DROP DATABASE IF EXISTS ${config.database}`
    );
  }


  createDatabase(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return this.getConnection().query(
      `CREATE DATABASE IF NOT EXISTS ${config.database}`
    );
  }

  useDatabase(environmentName) {
    const config = this.getEnvConfig(environmentName);

    return this.getConnection().query(
      `USE ${config.database}`
    );
  }

  setForeignKeyChecks(value) {
    // Make injected value safe
    var boolStr = value ? '1' : '0';

    return this.getConnection().query(
      `SET FOREIGN_KEY_CHECKS = ${boolStr}`
    );
  }

  getTables() {
    return ['contribution', 'donation', 'expenditure',
      'tag', 'expenditure_tag'];
  }

  truncateTables() {
    return Promise.all(
      this.getTables().map(table => this.truncateTable(table))
    );
  }

  truncateTable(table) {
    return this.getConnection().query(
      `TRUNCATE TABLE ${table}`
    );
  }

  /**
   * Close is synchronous so there is no returned promise
   */
  close() {
    this.getConnection().close();
  }

  getEnvConfig(environmentName) {
    if (!environmentName) {
      throw 'Please supply an environment name'
    }
    if (!this.config[environmentName]) {
      throw 'Cannot find database environment data'
    }

    return this.config[environmentName];
  }
}

现在,如果我运行测试,它们将通过并结束,但是有两个奇怪之处。首先,一些异步console.log输出将在测试摘要后输出,因此我认为我不是按照Jest希望的方式来处理异步。换句话说,我认为摘要应在所有这些之后呈现:

/project/node_modules/.bin/jest tests
  console.log
    Connect Jest database

      at Object.<anonymous> (tests/database/TestDemo.test.js:29:11)

  console.log
    Before a test

      at TestDatabase.beforeEachTest (tests/TestDatabase.js:10:13)

 PASS  tests/database/TestDemo.test.js
  Database tests
    ✓ Describe this demo test (72ms)
    ✓ Describe this demo test 2 (58ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.118s, estimated 3s
Ran all test suites matching /tests/i.
  console.log
    Test #1

      at Object.<anonymous> (tests/database/TestDemo.test.js:46:13)

  console.log
    Before a test

      at TestDatabase.beforeEachTest (tests/TestDatabase.js:10:13)

  console.log
    Test #2

      at Object.<anonymous> (tests/database/TestDemo.test.js:51:13)

  console.log
    Disconnect Jest database

      at _callee$ (tests/database/TestDemo.test.js:35:11)

您可以看到,两个测试的输出都显示在摘要之后,但是第一个测试的beforeEach的输出出现在测试摘要之前。

此外,如果我添加了使用该数据库的真实测试,则会收到错误消息,提示我有未处理的承诺,并且应该尝试Jest的未处理的承诺检测器(--detectOpenHandles)。而且,在这种情况下,Jest会循环停止并需要^ C来返回控制台提示。

因此,我正在尝试使用当前代码进行--detectOpenHandles,尽管我没有得到Jest冻结,但得到了以下内容。

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

  ●  TCPWRAP

      22 |     const config = this.getEnvConfig(environmentName);
      23 | 
    > 24 |     return mysql.createConnection({
         |                  ^
      25 |       host: config.host, user: config.username,
      26 |       password: config.password
      27 |     }).then((connection) => {

      at new Connection (node_modules/mysql2/lib/connection.js:35:27)
      at Object.<anonymous>.exports.createConnection (node_modules/mysql2/index.js:10:10)
      at Object.createConnection (node_modules/mysql2/promise.js:230:31)
      at TestDatabase.connect (tests/TestDatabase.js:24:18)
      at Object.<anonymous> (tests/database/TestDemo.test.js:30:19)

我的观点是,这与我通过更多测试获得的冻结直接相关,在尝试添加更多测试之前,我应该解决此问题。

我已经通过几个调查循环来确定可能导致这种情况的原因,并且代码已被多次调整:

  • [afterAllbeforeEach是异步操作,因此需要将它们return分配给Jest,因此Jest知道等待它们解决。
  • [afterAll会关闭数据库,但这不是异步的,因此我在这里使用Jest的done(),尽管如果没有done()也无法使用它。
  • [TestDatabase包含两个主要方法,beforeEachTestconnect,并且我非常小心以确保它们返回Promises。
  • 我更倾向于束缚物资,而不是异步等待。但是,我在几个方面尝试了async-await,但没有帮助。
  • dropDatabasecreateDatabasesetForeignKeyCheckstruncateTablestruncateTable之类的实用代码均返回承诺。
  • 我已经阅读了Jest async docs,并且有很多方法。主要要点是,如果您正在测试异步内容,则应将Promise返回给Jest,以便进行适当的等待。我真正的测试是同步的,这只是我之前的异步钩子。考虑一下,我想知道这是否是问题吗?

我对Jest相当陌生,也不太熟悉JS异步。每当我认为我对异步有了更好的理解时,就会得到新鲜的弯球。但是,我想知道这是否更多是开玩笑的怪癖,而不是难以理解原始异步。

回答如下:

目前,我已删除--detectOpenHandles并添加了--forceExit。顾名思义,它确保Jest在测试后退出,即使它认为存在未处理的承诺。

对我来说,该选项的存在很有趣-我想知道这意味着误报很普遍。无论哪种方式,我的测试确实确实通过了并且失败了,所以我将把这个问题放在后面的问题上。仍然非常欢迎获得非解决方法的令人满意的答案。

发布评论

评论列表 (0)

  1. 暂无评论