我正在尝试使用Sequelize和PostgreSQL设置测试夹具。但是,我写的测试有时会通过,有时会失败。错误的范围从SequelizeDatabaseError: type "participants_id_seq" already exists
到SequelizeUniqueConstraintError: Validation error
到SequelizeDatabaseError: relation "participants" does not exist
,这让我相信我已经设置了等待不正确,或者我错误地设置了同步。我已经尝试过使用async / await,而且我也尝试过使用回调设置Promise而没有任何运气。
在每次测试之前,我都会调用sync({ force: true })
,就像这样
const { sequelize, participants: Participants } = require('../../models');
const existingUserCredentials = {
teamName: 'TeamName',
firstName: 'FirstName',
lastName: 'LastName',
email: 'helloworld@helloworld.com',
password: 'helloworld',
};
const Fixture = async () => {
try {
await sequelize.sync({ force: true });
await Participants.create(existingUserCredentials);
} catch (err) {
logger.error(err);
throw err;
}
};
module.exports = {
Fixture
};
然后我会在我的测试用例中调用它:
describe('POST /login', () => {
beforeEach(async () => {
await Fixture();
});
it('throws unauthorized when user does not exist', async () => {
const { body, status } = await request(app)
.post('/api/login')
.send({
email: 'someemail@helloworld.com',
password: 'hunter123',
});
expect(body).toEqual({
message: messages.INVALID_LOGIN_CREDENTIALS,
});
expect(status).toEqual(HttpStatus.UNAUTHORIZED);
});
});
此测试将在某些时间通过,并且在其余时间内会因不同的错误而失败。
我的模型看起来像这样:
const bcrypt = require('bcryptjs');
module.exports = (sequelize, DataTypes) => {
const Participant = sequelize.define('participants', {
teamName: {
type: DataTypes.STRING,
allowNull: true,
},
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
set(password) {
const hash = bcrypt.hashSync(password, bcrypt.genSaltSync(10));
this.setDataValue('password', hash);
},
},
});
Participant.verifyPassword = (password, hash) =>
bcrypt.compareSync(password, hash);
return Participant;
};
我的迁移看起来像这样
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('participants', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
teamName: {
type: Sequelize.STRING,
allowNull: true,
},
firstName: {
type: Sequelize.STRING,
allowNull: false,
},
lastName: {
type: Sequelize.STRING,
allowNull: false,
},
email: {
type: Sequelize.STRING,
allowNull: false,
},
password: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: 'TIMESTAMP',
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
},
updatedAt: {
allowNull: false,
type: 'TIMESTAMP',
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
},
}),
down: (queryInterface) => queryInterface.dropTable('participants'),
};
我是否正确设置了这个,所以每个测试都可以有一个干净的数据库?谢谢你的帮助!
答案 0 :(得分:1)
我明白了。事实证明,Jest并行运行测试用例,因此多个测试用例同时使用了测试数据库。真正的解决方案是通过将--runInBand
标志添加到测试命令
// package.json
{
...
"scripts": {
...
"test": "cross-env NODE_ENV=test jest --runInBand"
}
...
}
但是,这会减慢运行测试用例所需的时间。更好的解决方案可能是模拟数据库,而不是使用实际的测试数据库。还有一些,例如sequelize-mock和sequelize-mocking。
所以,我无法用PostgreSQL来解决这个问题。取而代之的是,我将Sequelize配置切换为使用SQLite进行测试,并且工作正常。这是我的配置
module.exports = {
development: {
username: process.env.PGUSER,
password: process.env.PGPASSWORD,
database: process.env.PGDATABASE,
host: process.env.PGHOST || '127.0.0.1',
dialect: 'postgres',
operatorsAliases: false,
},
test: {
username: process.env.SQLITEUSER,
password: process.env.SQLITEPASSWORD,
database: process.env.SQLITEDATABASE,
host: process.env.PGHOST || '127.0.0.1',
dialect: 'sqlite',
operatorsAliases: false,
},
production: {
username: process.env.PGUSER,
password: process.env.PGPASSWORD,
database: process.env.PGPRODDATABASE,
host: process.env.PGHOST || '127.0.0.1',
dialect: 'postgres',
operatorsAliases: false,
logging: false,
},
};
我会打开这个问题,因为我不认为这是一个很好的解决方案