javascript - 如何确保Jest bootstrap文件首先运行?

标签 javascript async-await jestjs es6-promise

介绍

我陷入了在Jest测试框架中使用的数据库Promises的困扰。事情以错误的顺序运行,并且按照我的一些最新更改,Jest无法正确完成,因为未处理未知的异步操作。我是Node / Jest的新手。

这是我想要做的。我正在多个Docker容器环境中设置Jest来调用内部API,以测试其JSON输出,并运行服务功能,以查看它们对测试环境MySQL数据库所做的更改。为此,我是:


使用setupFilesAfterEnv笑话配置选项指向安装文件,我相信应该首先运行
使用安装文件破坏测试数据库(如果存在),重新创建它,然后创建一些表
使用mysql2/promise对数据库进行操作
在测试中使用beforeEach(() => {})截断所有表,以准备插入每个测试数据,从而使测试彼此不依赖


我可以确认Jest的安装文件正在第一个(也是唯一的)测试文件之前运行,但是奇怪的是,测试文件中的Promise catch()似乎在安装文件中的finally之前抛出了。

我将首先放下代码,然后推测出我隐约怀疑是什么问题。



这是安装文件,简洁明了:

// Little fix for Jest, see https://stackoverflow.com/a/54175600
require('mysql2/node_modules/iconv-lite').encodingExists('foo');

// Let's create a database/tables here
const mysql = require('mysql2/promise');
import TestDatabase from './TestDatabase';
var config = require('../config/config.json');

console.log('Here is the bootstrap');

const initDatabase = () => {
  let database = new TestDatabase(mysql, config);
  database.connect('test').then(() => {
    return database.dropDatabase('contributor_test');
  }).then(() => {
    return database.createDatabase('contributor_test');
  }).then(() => {
    return database.useDatabase('contributor_test');
  }).then(() => {
    return database.createTables();
  }).then(() => {
    return database.close();
  }).finally(() => {
    console.log('Finished once-off db setup');
  });
};
initDatabase();


config.json只是用户名/密码,不值得在此处显示。

如您所见,此代码使用实用程序数据库类,即:

export default class TestDatabase {

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

  async connect(environmentName) {
    if (!environmentName) {
      throw 'Please supply an environment name to connect'
    }
    if (!this.config[environmentName]) {
      throw 'Cannot find db environment data'
    }

    const config = this.config[environmentName];
    this.connection = await this.mysql.createConnection({
      host: config.host, user: config.username,
      password: config.password,
      database: 'contributor'
    });
  }

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

    return this.connection;
  }

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

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

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

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

  /**
   * This will be replaced with the migration system
   */
  createTables() {
    return Promise.all(
      this.getTables().map(table => this.createTable(table))
    );
  }

  /**
   * This will be replaced with the migration system
   */
  createTable(table) {
    return this.getConnection().query(
      `CREATE TABLE IF NOT EXISTS ${table} (id INTEGER)`
    );
  }

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

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

  close() {
    this.getConnection().close();
  }

}


最后,这是实际测试:

const mysql = require('mysql2/promise');
import TestDatabase from '../TestDatabase';
var config = require('../../config/config.json');

let database = new TestDatabase(mysql, config);

console.log('Here is the test class');


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

  beforeEach(() => {
    database.connect('test').then(() => {
      return database.useDatabase('contributor_test');
    }).then (() => {
      return database.truncateTables();
    }).catch(() => {
      console.log('Failed to clear down database');
    });
  });

  afterAll(async () => {
    await database.getConnection().close();
  });

  test('Describe this demo test', () => {
    expect(true).toEqual(true);
  });

});


输出量

如您所见,我有一些控制台日志,这是他们的意外顺序:


“这是引导程序”
“这是考试班”
测试在这里完成
“无法清除数据库”
“完成一次性数据库设置”
Jest报告说:“ Jest在测试运行完成后一秒钟没有退出。这通常意味着您的测试中没有停止异步操作。”
笑话挂起,需要^ C退出


我想要:


“这是引导程序”
“完成一次性数据库设置”
“这是考试班”
调用truncateTables时没有错误


我怀疑数据库错误是TRUNCATE操作失败,因为表尚不存在。当然,如果命令以正确的顺序运行,它们将会!

笔记

我最初是导入mysql而不是mysql/promise的,并且在Stack Overflow上的其他地方发现,在没有承诺的情况下,需要向每个命令添加回调。这将使安装文件变得混乱-每个操作都需要连接,删除db,创建db,使用db,创建表,关闭,这些操作都需要出现在深度嵌套的回调结构中。我可能可以做到,但是有点讨厌。

我还尝试使用await针对所有应许返回的db操作写入安装文件。但是,这意味着我必须将initDatabase声明为async,我认为这意味着我不能再保证整个安装文件都首先运行,从本质上讲,这与我现在遇到的问题相同。

我已经注意到TestDatabase中的大多数实用程序方法都返回一个诺言,对此我感到非常满意。但是connect很奇怪-我希望它存储连接,因此对于Promise不是连接,我是否可以返回Promise感到困惑。我刚刚尝试使用.then()存储连接,如下所示:

    return this.mysql.createConnection({
      host: config.host, user: config.username,
      password: config.password
    }).then((connection) => {
      this.connection = connection;
    });


我想知道这是否行得通,因为可伸缩链应该在连接承诺解决之前先进入列表中的下一个内容。但是,会产生相同的错误。

我简要地认为,使用两个连接可能是一个问题,以防在关闭该连接之前看不到在一个连接中创建的表。基于这个想法,也许我应该尝试连接安装文件并以某种方式重新使用该连接(例如,使用mysql2连接池)。但是我的感觉告诉我这确实是一个Promise问题,在Jest尝试继续测试执行之前,我需要弄清楚如何在安装文件中完成数据库初始化。

接下来我可以尝试什么?如果这是一种更好的方法,我可以放弃mysql2/promise并退回mysql,但我宁愿坚持(并且完全不屑一顾)诺言。

最佳答案

您需要在awaitdatabase.connect()您的beforeEach()

关于javascript - 如何确保Jest bootstrap文件首先运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61637311/

相关文章:

reactjs - 用 Jest 进行快照测试时无法模拟库值

javascript - 在 HTML5 Canvas 上绘制拉伸(stretch)弧

javascript - 从 symfony Controller 返回 json 数组,以便在 highcharts.js 上使用

c# - 是否有比在异步方法中调用 ConfigureAwait(false) 更具可读性的替代方法?

c# - ASP.NET MVC 5 。异步等待。等待具有不同返回类型的任务

reactjs - 如何使用 Jest.js 对 React 组件的样式进行单元测试?

javascript - jQuery 在与 PHP 动态 ID 相关的 Bootstrap Popover 按钮上执行 URL 操作

javascript - 如何获取特定类的表数据总数? (jQuery)

c# - 创建循环直到 true 或超时的等待任务

javascript - 用 enzyme 测试支柱钻井