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

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连接和模型缓存,导致多测试用例运行时旧实例与新实例的模型定义冲突;同时存在未等待的异步操作,引发数据不一致。

具体修复步骤

  1. 添加测试后清理逻辑
    在顶级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]
    })
  })

  // ... 其余测试代码
})
  1. 修复未等待的异步操作
    在Login User的beforeEach中,给user.update()添加await,确保密码哈希更新完成后再执行测试:
describe('Login User', () => {
  beforeEach(async () => {
    const hashedPass = await bcrypt.hash('teste', 10)
    await user.update({ password: hashedPass }) // 加入await
  })
  // ... 其余测试代码
})
  1. 确保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'
      ),
    })
  )
})
  1. 可选:统一表名配置
    在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 09:35:53