I am down to one failed unit test and for the past 3 days I have not been able to pass it. I am getting the followning error in the console related to the jest test:
FAIL src/user/user.service.spec.ts (10.795 s)
● UserService › create › should create a new user
InternalServerErrorException: this.userModel is not a constructor
23 | throw error;
24 | }
> 25 | throw new InternalServerErrorException(error.message);
| ^
26 | }
27 | }
28 |
at UserService.create (user/user.service.ts:25:13)
at Object.<anonymous> (user/user.service.spec.ts:76:22)
Here is what I have for my user.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { getModelToken } from '@nestjs/mongoose';
import { UserService } from './user.service';
import { User, UserDocument } from '../schemas/user.schema';
import { ConflictException, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { Model } from 'mongoose';
describe('UserService', () => {
let service: UserService;
let userModel: Model<UserDocument>;
const mockUser = {
_id: 'someId',
username: 'testuser',
email: '[email protected]',
password: 'hashedpassword',
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getModelToken(User.name),
useValue: {
new: jest.fn().mockResolvedValue(mockUser),
constructor: jest.fn().mockResolvedValue(mockUser),
findOne: jest.fn(),
findById: jest.fn(),
find: jest.fn(),
create: jest.fn(),
deleteOne: jest.fn(),
deleteMany: jest.fn(),
exec: jest.fn(),
},
},
],
}).compile();
service = module.get<UserService>(UserService);
userModel = module.get<Model<UserDocument>>(getModelToken(User.name));
});
afterEach(() => {
jest.clearAllMocks();
});
describe('create', () => {
it('should create a new user', async () => {
const createUserDto: CreateUserDto = {
username: 'newuser',
email: '[email protected]',
password: 'password123',
};
// Mock findOne to return null (user doesn't exist)
jest.spyOn(userModel, 'findOne').mockReturnValue({
exec: jest.fn().mockResolvedValue(null),
} as any);
// Mock the 'create' operation on the model
const mockCreatedUser = {
_id: 'someNewId',
...createUserDto,
save: jest.fn().mockResolvedValue({
_id: 'someNewId',
...createUserDto,
}),
};
jest.spyOn(userModel, 'create').mockResolvedValue(mockCreatedUser as any);
const result = await service.create(createUserDto);
expect(result).toEqual(expect.objectContaining({
username: createUserDto.username,
email: createUserDto.email,
}));
// Verify that findOne was called with the correct parameters
expect(userModel.findOne).toHaveBeenCalledWith({
$or: [{ username: createUserDto.username }, { email: createUserDto.email }]
});
// Verify that 'create' was called with the correct parameters
expect(userModel.create).toHaveBeenCalledWith(createUserDto);
});
it('should throw ConflictException if username or email already exists', async () => {
const createUserDto: CreateUserDto = {
username: 'existinguser',
email: '[email protected]',
password: 'password123',
};
// Mock findOne to return an existing user
jest.spyOn(userModel, 'findOne').mockReturnValue({
exec: jest.fn().mockResolvedValue(mockUser),
} as any);
await expect(service.create(createUserDto)).rejects.toThrow(ConflictException);
// Verify that findOne was called with the correct parameters
expect(userModel.findOne).toHaveBeenCalledWith({
$or: [{ username: createUserDto.username }, { email: createUserDto.email }]
});
});
});
describe('findOne', () => {
it('should find a user by username', async () => {
jest.spyOn(userModel, 'findOne').mockReturnValue({
exec: jest.fn().mockResolvedValue(mockUser),
} as any);
const result = await service.findOne('testuser');
expect(result).toEqual(mockUser);
});
it('should throw NotFoundException if user not found', async () => {
jest.spyOn(userModel, 'findOne').mockReturnValue({
exec: jest.fn().mockResolvedValue(null),
} as any);
await expect(service.findOne('nonexistent')).rejects.toThrow(NotFoundException);
});
});
describe('findById', () => {
it('should find a user by id', async () => {
jest.spyOn(userModel, 'findById').mockReturnValue({
exec: jest.fn().mockResolvedValue(mockUser),
} as any);
const result = await service.findById('someId');
expect(result).toEqual(mockUser);
});
it('should throw NotFoundException if user not found', async () => {
jest.spyOn(userModel, 'findById').mockReturnValue({
exec: jest.fn().mockResolvedValue(null),
} as any);
await expect(service.findById('nonexistentId')).rejects.toThrow(NotFoundException);
});
});
describe('findAll', () => {
it('should return an array of users', async () => {
jest.spyOn(userModel, 'find').mockReturnValue({
exec: jest.fn().mockResolvedValue([mockUser]),
} as any);
const result = await service.findAll();
expect(result).toEqual([mockUser]);
});
it('should throw InternalServerErrorException on database error', async () => {
jest.spyOn(userModel, 'find').mockReturnValue({
exec: jest.fn().mockRejectedValue(new Error('Database error')),
} as any);
await expect(service.findAll()).rejects.toThrow(InternalServerErrorException);
});
});
describe('deleteById', () => {
it('should delete a user by id', async () => {
jest.spyOn(userModel, 'deleteOne').mockReturnValue({
exec: jest.fn().mockResolvedValue({ deletedCount: 1 }),
} as any);
const result = await service.deleteById('someId');
expect(result).toBe(true);
});
it('should throw NotFoundException if user not found', async () => {
jest.spyOn(userModel, 'deleteOne').mockReturnValue({
exec: jest.fn().mockResolvedValue({ deletedCount: 0 }),
} as any);
await expect(service.deleteById('nonexistentId')).rejects.toThrow(NotFoundException);
});
});
describe('deleteAll', () => {
it('should delete all users', async () => {
jest.spyOn(userModel, 'deleteMany').mockReturnValue({
exec: jest.fn().mockResolvedValue({}),
} as any);
await expect(service.deleteAll()).resolves.not.toThrow();
});
it('should throw InternalServerErrorException on database error', async () => {
jest.spyOn(userModel, 'deleteMany').mockReturnValue({
exec: jest.fn().mockRejectedValue(new Error('Database error')),
} as any);
await expect(service.deleteAll()).rejects.toThrow(InternalServerErrorException);
});
});
});
and finally, here is what I have for the user.service.ts
import { Injectable, ConflictException, NotFoundException, InternalServerErrorException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from '../schemas/user.schema';
import { CreateUserDto } from './dto/create-user.dto';
@Injectable()
export class UserService {
constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserDto: CreateUserDto): Promise<User> {
try {
const existingUser = await this.userModel.findOne({
$or: [{ username: createUserDto.username }, { email: createUserDto.email }]
}).exec();
if (existingUser) {
throw new ConflictException('Username or email already exists');
}
const user = new this.userModel(createUserDto);
return await user.save();
} catch (error) {
if (error instanceof ConflictException) {
throw error;
}
throw new InternalServerErrorException(error.message);
}
}
async findOne(username: string): Promise<User> {
const user = await this.userModel.findOne({ username }).exec();
if (!user) {
throw new NotFoundException(`User with username ${username} not found`);
}
return user;
}
async findById(id: string): Promise<User> {
const user = await this.userModel.findById(id).exec();
if (!user) {
throw new NotFoundException(`User with id ${id} not found`);
}
return user;
}
async findAll(): Promise<User[]> {
try {
return await this.userModel.find().exec();
} catch (error) {
throw new InternalServerErrorException(error.message);
}
}
async deleteById(id: string): Promise<boolean> {
const result = await this.userModel.deleteOne({ _id: id }).exec();
if (result.deletedCount === 0) {
throw new NotFoundException(`User with id ${id} not found`);
}
return true;
}
async deleteAll(): Promise<void> {
try {
await this.userModel.deleteMany().exec();
} catch (error) {
throw new InternalServerErrorException(error.message);
}
}
}
Test Suites: 1 failed, 18 passed, 19 total
Tests: 1 failed, 94 passed, 95 total
If anyone has dealt with this before and can share with me what worked for you, I would sure appreciate it. If I am missing any code that you would like to see, please tell me and I will be happy to edit my question. Thank you in advance.