sequelize.sync({force:true})有时无法正确设置数据库

时间:2017-12-15 14:14:27

标签: javascript postgresql sequelize.js jestjs

我正在尝试使用Sequelize和PostgreSQL设置测试夹具。但是,我写的测试有时会通过,有时会失败。错误的范围从SequelizeDatabaseError: type "participants_id_seq" already existsSequelizeUniqueConstraintError: Validation errorSequelizeDatabaseError: 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'),
};

我是否正确设置了这个,所以每个测试都可以有一个干净的数据库?谢谢你的帮助!

1 个答案:

答案 0 :(得分:1)

更新

我明白了。事实证明,Jest并行运行测试用例,因此多个测试用例同时使用了测试数据库。真正的解决方案是通过将--runInBand标志添加到测试命令

来顺序运行测试用例
// package.json
{
    ...
    "scripts": {
        ...
        "test": "cross-env NODE_ENV=test jest --runInBand"
    }
    ...
}

但是,这会减慢运行测试用例所需的时间。更好的解决方案可能是模拟数据库,而不是使用实际的测试数据库。还有一些,例如sequelize-mocksequelize-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,
  },
};

我会打开这个问题,因为我不认为这是一个很好的解决方案