如何为依赖Repository<entity>的Auth服务编写测试?解决循环JSON错误
测试AuthService时出现循环引用转JSON错误
运行测试时持续出现以下错误:
TypeError: Converting circular structure to JSON --> starting at object with constructor 'NestContainer' | property 'modules' -> object with constructor 'ModulesContainer' | property 'InternalCoreModule' -> object with constructor 'Module' --- property 'container' closes the circle at stringify (<anonymous>)
相关代码
auth.service.spec.ts
import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; import { Test, TestingModule } from '@nestjs/testing'; import { UsersService } from '../users/users.service'; import { UsersModule } from '../users/users.module'; import { AuthService } from './auth.service'; import { jwtConstants } from './strategies/constants'; import { JwtStrategy } from './strategies/jwt.strategy'; describe('AuthService', () => { let service: AuthService; beforeEach(async () => { const moduleRef: TestingModule = await Test.createTestingModule({ imports: [ UsersModule, PassportModule, JwtModule.register({ secret: jwtConstants.secret, signOptions: { expiresIn: '60s' }, }), ], providers: [ AuthService, JwtStrategy, UsersService ], }).compile(); service = moduleRef.get<AuthService>(AuthService); }); it('should be defined', () => { expect(service).toBeDefined(); }); });
auth.service.ts
import { Injectable, NotFoundException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Users } from '../db/entities/users.entity'; import { UsersService } from '../users/users.service'; import * as bcrypt from 'bcrypt'; export class User { username: string; password: string; } @Injectable() export class AuthService { constructor( private readonly usersService: UsersService, private readonly jwtService: JwtService, ) {} async validateUser( username: string, password: string, ): Promise<Users | undefined> { const user = await this.usersService.findOne(username); const isPasswordCorrect = await bcrypt.compare( password, user?.passwordHash, ); if (isPasswordCorrect) { return user; } return null; } async login(userRequest: any) { const response = await this.validateUser( userRequest.username, userRequest.password, ); if (response && response !== undefined) { const payload = { username: response?.userName, sub: response?.id, }; return { access_token: this.jwtService.sign(payload), }; } throw new NotFoundException( 'This combination of user and password does not exist.', ); } }
users.service.ts
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Users } from '../db/entities/users.entity'; import { Repository } from 'typeorm'; @Injectable() export class UsersService { constructor( @InjectRepository(Users) private userRepository: Repository<Users>, ) {} async findOne(username: string): Promise<Users | undefined> { return this.userRepository.findOneBy({ userName: username, }); } }
解决方案
错误根源是测试模块同时导入UsersModule并声明UsersService为provider,导致Nest容器出现循环引用;同时真实UsersService依赖TypeORM的Repository,测试环境未正确配置数据库连接,进一步引发问题。
修改步骤:
- 移除
UsersModule导入,避免加载不必要的依赖和循环 - 模拟
UsersService,替代真实服务以避免依赖数据库操作 - 调整测试模块的providers配置,替换真实
UsersService为模拟对象
修改后的auth.service.spec.ts代码:
import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; import { Test, TestingModule } from '@nestjs/testing'; import { UsersService } from '../users/users.service'; import { AuthService } from './auth.service'; import { jwtConstants } from './strategies/constants'; import { JwtStrategy } from './strategies/jwt.strategy'; // 模拟UsersService const mockUsersService = { findOne: jest.fn().mockResolvedValue({ id: 1, userName: 'testuser', passwordHash: '$2b$10$exampleHash', // 提前加密的测试密码 }), }; describe('AuthService', () => { let service: AuthService; let usersService: UsersService; beforeEach(async () => { const moduleRef: TestingModule = await Test.createTestingModule({ imports: [ PassportModule, JwtModule.register({ secret: jwtConstants.secret, signOptions: { expiresIn: '60s' }, }), ], providers: [ AuthService, JwtStrategy, // 提供模拟的UsersService { provide: UsersService, useValue: mockUsersService, }, ], }).compile(); service = moduleRef.get<AuthService>(AuthService); usersService = moduleRef.get<UsersService>(UsersService); }); it('should be defined', () => { expect(service).toBeDefined(); }); // 可添加更多测试用例,比如验证validateUser和login方法 it('should validate user correctly', async () => { const result = await service.validateUser('testuser', 'testpassword'); expect(result).toBeDefined(); expect(usersService.findOne).toHaveBeenCalledWith('testuser'); }); });
额外说明
- 可根据测试需求调整模拟
UsersService的返回值,比如模拟用户不存在的场景,测试登录失败逻辑 - 若需更复杂的模拟,可使用
jest.spyOn替换特定方法,而非完全替换整个服务 - 确保
bcrypt在测试环境正常工作,也可模拟bcrypt.compare方法,避免实际加密操作
内容的提问来源于stack exchange,提问作者Miguel Angel Segura Sandoval
相关产品推荐
相关产品推荐

