Я пытаюсь настроить тестовое оборудование с помощью Sequelize и PostgreSQL. Однако те тесты, которые я написал, иногда проходят, а иногда терпят неудачу. Диапазон ошибок от SequelizeDatabaseError: type "participants_id_seq" already exists
для SequelizeUniqueConstraintError: Validation error
для SequelizeDatabaseError: relation "participants" does not exist
, что заставляет меня думать, что либо я настроил ожидание неправильно, либо настроил синхронизация некорректна. Я попытался использовать async/wait, и я также попытался настроить Promises с обратными вызовами без везения.
Перед каждым тестом я вызываю sync({ force: true })
, например
const { sequelize, participants: Participants } = require('../../models');
const existingUserCredentials = {
teamName: 'TeamName',
firstName: 'FirstName',
lastName: 'LastName',
email: '[email protected]',
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: '[email protected]',
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'),
};
Я правильно настраиваю это, чтобы у меня была чистая база данных для каждого теста? Спасибо за помощь!
Я понял. Оказывается, что Jest запускает тестовые примеры параллельно, поэтому тестовая база данных использовалась одновременно несколькими тестовыми примерами. Реальное решение состоит в том, чтобы последовательно запускать тестовые --runInBand
флаг --runInBand
к команде test
// 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,
},
};
Я собираюсь оставить этот вопрос открытым, хотя, поскольку я действительно не думаю, что это хорошее решение