You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sequelize连接管理器关闭后调用getConnection的测试问题排查

问题:Sequelize集成测试随机报错ConnectionManager.getConnection was called after the connection manager was closed!

我在对基于Node.js、Express、Jest和Sequelize的API做集成测试时,随机出现错误:Sequelize ConnectionManager.getConnection was called after the connection manager was closed!。该问题仅出现在用户功能的集成测试中,用Postman调用API完全正常。

我已尝试调整连接池参数、更换数据创建/更新方式,但始终无法解决。测试失败具有随机性,时而正常时而报错,但错误信息一致,调试确认是Sequelize在执行更新或删除操作时触发该错误。


相关代码

用户模型

'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
  }
  User.init({
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      allowNull: false,
      primaryKey: true,
    },
    full_name: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    is_active: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: true,
    },
  }, 
  {
    sequelize,
    modelName: 'User',
    tableName: 'user',
    timestamps: false,
    hooks: {
      afterCreate: (record) => {
        delete record.dataValues.password;
        delete record.dataValues.is_active;
      }
    }
  });
  return User;
};

用户仓库方法示例

async delete (id) {
        try {
            const t = await db.sequelize.transaction();
            await User.update(
                { is_active: false },
                {
                    where: {id: id},
                    transaction: t
                }
            );
            await t.commit();
        } catch (error) {
            await t.rollback();
            throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR,'Error while deleting user');
        }
    };

集成测试代码

const httpStatus = require('../../src/utils/statusCodes');
const request = require('supertest');
const app = require('../../app');
const db = require('../../src/database/models');
const User = db.user;
const Token = db.token;

describe('Testing user feature', () => {
  let tempUser;
  let token;
  beforeAll(async () => {     
    tempUser = await User.create({
      id: 100000,
      full_name: 'Porra',
      email: 'porra@gmail.com',
      password: '$2b$10$OMDQ.q5dkZAZkQH1g5W6IOP4ZLCwBV4xnTCHDng2pNhlWOpq/n5xO',
      created_at: new Date(),
      updated_at: new Date(),
      is_active: true
    }); 
    const loginResponse = await request(app).post('/login').send({"email": tempUser.email, "password": "1234"});
    token = loginResponse.body.token;
  });
  afterAll(async () => {
    await Token.destroy({ where: { user_id: tempUser.id } });
    await User.destroy({ where: { id: tempUser.id } });
    await db.sequelize.close();  
  });
  afterEach(async () => {
    await db.sequelize.closeConnections();
  });

  it('Should update a user', async () => {
    const updatedUser = await request(app).put(`/users/${tempUser.id}`).set('Authorization', token).send({
      full_name: 'Tadeuzinho Smith'
    });
    expect(updatedUser.status).toBe(httpStatus.OK);
    expect(updatedUser.body.details).toBe('User updated successfully');
  });
  it('Should delete a user', async () => {
    const deletedUser = await request(app).delete(`/users/${tempUser.id}`).set('Authorization', token);
    expect(deletedUser.status).toBe(200);
    expect(deletedUser.body.details).toBe('User deleted successfully');
  });
});

问题原因

测试代码中afterEach钩子调用了db.sequelize.closeConnections(),会关闭所有当前连接。但Jest默认并行执行测试,第一个测试结束后关闭连接,第二个测试再执行数据库操作时,连接管理器已被破坏,从而触发报错。另外afterAll中的db.sequelize.close()会彻底关闭连接管理器,若测试未完全结束,后续操作也会报错。

修复方案

  1. 移除afterEach中的连接关闭操作:不要在每个测试后关闭连接,让连接池复用连接即可。
  2. 用事务隔离测试数据:替代关闭连接的方式,在beforeEach启动事务,afterEach回滚事务,既隔离测试数据,又不影响连接池。
  3. 完善事务异常处理:确保仓库方法中事务未创建时不会调用回滚,避免额外报错。

修改后的测试代码

const httpStatus = require('../../src/utils/statusCodes');
const request = require('supertest');
const app = require('../../app');
const db = require('../../src/database/models');
const User = db.user;
const Token = db.token;

describe('Testing user feature', () => {
  let tempUser;
  let token;
  let transaction;

  beforeAll(async () => {     
    tempUser = await User.create({
      id: 100000,
      full_name: 'Porra',
      email: 'porra@gmail.com',
      password: '$2b$10$OMDQ.q5dkZAZkQH1g5W6IOP4ZLCwBV4xnTCHDng2pNhlWOpq/n5xO',
      created_at: new Date(),
      updated_at: new Date(),
      is_active: true
    }); 
    const loginResponse = await request(app).post('/login').send({"email": tempUser.email, "password": "1234"});
    token = loginResponse.body.token;
  });

  beforeEach(async () => {
    // 启动事务用于测试数据隔离
    transaction = await db.sequelize.transaction();
  });

  afterEach(async () => {
    // 回滚事务,不污染原始数据
    await transaction.rollback();
  });

  afterAll(async () => {
    // 清理测试数据后再关闭连接
    await Token.destroy({ where: { user_id: tempUser.id } });
    await User.destroy({ where: { id: tempUser.id } });
    await db.sequelize.close();  
  });

  it('Should update a user', async () => {
    const updatedUser = await request(app).put(`/users/${tempUser.id}`).set('Authorization', token).send({
      full_name: 'Tadeuzinho Smith'
    });
    expect(updatedUser.status).toBe(httpStatus.OK);
    expect(updatedUser.body.details).toBe('User updated successfully');
  });

  it('Should delete a user', async () => {
    const deletedUser = await request(app).delete(`/users/${tempUser.id}`).set('Authorization', token);
    expect(deletedUser.status).toBe(200);
    expect(deletedUser.body.details).toBe('User deleted successfully');
  });
});

修改后的仓库方法

async delete (id) {
  let t;
  try {
    t = await db.sequelize.transaction();
    await User.update(
      { is_active: false },
      {
        where: {id: id},
        transaction: t
      }
    );
    await t.commit();
  } catch (error) {
    // 确保事务存在时再回滚
    if (t) await t.rollback();
    throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR,'Error while deleting user');
  }
};

内容的提问来源于stack exchange,提问作者Louis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 16:14:53