Sequelize批量测试异常:提示找不到Users表描述
问题
单独运行单个测试用例时一切正常,但运行全部测试时,控制台持续输出错误:
No description found for "Users" table. Check the table name and schema; remember, they _are_ case sensitive.
测试代码
import { Sequelize } from 'sequelize-typescript' import bcrypt from 'bcrypt' import request from 'supertest' import User from '../../src/Models/UserModel' import app from '../../src/app' import { setupSequelize } from '../setup/sequelizeSetup' import RefreshTokenService from '../../src/Services/RefreshTokenServices' import { signJwt } from '../../src/utils/JwtUtils' describe('User Controller', () => { let user: User let sequelize: Sequelize beforeEach(async () => { sequelize = await setupSequelize() user = await User.create({ email: 'teste@email.com', name: 'teste', password: 'teste', }) }) describe('Register User', () => { it('Deve registrar um usuário com sucesso', async () => { const response = await request(app) .post('/user/register') .send({ name: 'teste', email: 'teste2@email.com', password: 'testeteste', }) .expect(200) expect(response.body).toEqual( expect.objectContaining({ message: expect.stringContaining('Usuário Criado com Sucesso'), }) ) }) it('Deve retornar erro 400 ao registrar usuário sem nome', async () => { const response = await request(app) .post('/user/register') .send({ email: 'teste2@email.com', password: 'teste' }) .expect(400) expect(response.body).toEqual( expect.objectContaining({ error: expect.stringContaining( 'Não há o nome do usuário, lembre de adicionar o campo `name`' ), }) ) }) it('Deve retornar erro 400 ao registrar usuário sem email', async () => { const response = await request(app) .post('/user/register') .send({ name: 'teste', password: 'teste' }) .expect(400) expect(response.body).toEqual( expect.objectContaining({ error: expect.stringContaining( 'Não há o nome de email, lembre de adicionar o campo `email`' ), }) ) }) it('Deve retornar erro 400 ao registrar usuário sem senha', async () => { const response = await request(app) .post('/user/register') .send({ name: 'teste', email: 'teste2@email.com' }) .expect(400) expect(response.body).toEqual( expect.objectContaining({ error: expect.stringContaining( 'Não há senha para registrar-se, lembre de adicionar o campo `password`' ), }) ) }) }) describe('Login User', () => { beforeEach(async () => { const hashedPass = await bcrypt.hash('teste', 10) user.update({ password: hashedPass }) }) it('Deve logar um usuário com sucesso', async () => { const response = await request(app) .post('/user/login') .send({ email: 'teste@email.com', password: 'teste' }) expect(response.body).toEqual( expect.objectContaining({ message: expect.stringContaining('Login realizado com sucesso'), token: expect.stringMatching(/.{680,}/), }) ) }) it('Deve retornar erro 400 ao logar usuário sem email', async () => { const response = await request(app) .post('/user/login') .send({ password: 'teste' }) .expect(400) expect(response.body).toEqual( expect.objectContaining({ error: expect.stringContaining( 'Não há o nome de email, lembre de adicionar o campo `email`' ), }) ) }) it('Deve retornar erro 400 ao logar usuário sem senha', async () => { const response = await request(app) .post('/user/login') .send({ email: 'teste@email.com' }) .expect(400) expect(response.body).toEqual( expect.objectContaining({ error: expect.stringContaining( 'Não há senha para registrar-se, lembre de adicionar o campo `password`' ), }) ) }) }) describe('Logout User', () => { it('Deve deslogar um usuário com sucesso', async () => { const newAccessToken = signJwt(user.id, 'Access') RefreshTokenService.create(newAccessToken, 1) const response = await request(app) .get('/user/logout') .set('Authorization', `Bearer ${newAccessToken}`) expect(response.body).toEqual( expect.objectContaining({ message: expect.stringContaining( 'Você saiu da sua conta com sucesso' ), }) ) }) }) })
Sequelize配置代码
// tests/setup/sequelize.ts import { Sequelize } from 'sequelize-typescript' import Account from '../../src/Models/AccountModel' import EmailConfirmToken from '../../src/Models/EmailConfirmTokenModel' import RedefinePasswordTokens from '../../src/Models/RedefinePasswordTokensModel' import RefreshToken from '../../src/Models/RefreshTokenModel' import User from '../../src/Models/UserModel' export const setupSequelize = async () => { const sequelize: Sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', models: [ Account, EmailConfirmToken, RedefinePasswordTokens, RefreshToken, User, ], logging: false, }) await sequelize.sync({ force: true }) return sequelize }
解决方案
核心原因
测试全程未清理Sequelize连接和模型缓存,导致多测试用例运行时旧实例与新实例的模型定义冲突;同时存在未等待的异步操作,引发数据不一致。
具体修复步骤
- 添加测试后清理逻辑
在顶级describe中加入afterEach,确保每个测试完成后关闭连接并清除模型缓存:
describe('User Controller', () => { let user: User let sequelize: Sequelize beforeEach(async () => { sequelize = await setupSequelize() user = await User.create({ email: 'teste@email.com', name: 'teste', password: 'teste', }) }) // 新增清理逻辑 afterEach(async () => { await sequelize.close() // 清除模型缓存,避免重复注册冲突 Object.keys(sequelize.models).forEach(key => { delete sequelize.models[key] }) }) // ... 其余测试代码 })
- 修复未等待的异步操作
在Login User的beforeEach中,给user.update()添加await,确保密码哈希更新完成后再执行测试:
describe('Login User', () => { beforeEach(async () => { const hashedPass = await bcrypt.hash('teste', 10) await user.update({ password: hashedPass }) // 加入await }) // ... 其余测试代码 })
- 确保RefreshTokenService操作异步完成
如果RefreshTokenService.create()是异步方法,同样需要添加await:
it('Deve deslogar um usuário com sucesso', async () => { const newAccessToken = signJwt(user.id, 'Access') await RefreshTokenService.create(newAccessToken, 1) // 加入await const response = await request(app) .get('/user/logout') .set('Authorization', `Bearer ${newAccessToken}`) expect(response.body).toEqual( expect.objectContaining({ message: expect.stringContaining( 'Você saiu da sua conta com sucesso' ), }) ) })
- 可选:统一表名配置
在User模型中显式指定表名,避免Sequelize自动复数化导致的大小写/名称不匹配:
// src/Models/UserModel.ts import { Table, Model, Column } from 'sequelize-typescript' @Table({ tableName: 'Users' }) // 与错误提示中的表名保持一致 export default class User extends Model { // ... 你的模型字段定义 }
内容的提问来源于stack exchange,提问作者Kobra
相关产品推荐
相关产品推荐

