介绍
我陷入了在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
,但我宁愿坚持(并且完全不屑一顾)诺言。
最佳答案
您需要在await
中database.connect()
您的beforeEach()
。
关于javascript - 如何确保Jest bootstrap文件首先运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61637311/