如何用 Jest 嘲笑拒绝承诺
How to mock a promise rejection with Jest
我在做什么?
我正在学习 nestjs 课程,其中有一些单元测试。我编写了这个测试来检查存储库 class 中的 signUp 方法。问题是为了触发异常,行 user.save()
应该 return 承诺拒绝(模拟一些写入数据库的问题)。我尝试了几种方法(见下文),但 none 有效。
问题
结果是测试成功,但是有一个unhandled Promise rejection
。这样即使我断言 not.toThow()
它也会以相同的 unhandled Promise rejection
成功
(node:10149) UnhandledPromiseRejectionWarning: Error: expect(received).rejects.toThrow()
Received promise resolved instead of rejected
Resolved to value: undefined
(Use `node --trace-warnings ...` to show where the warning was created)
如何让它正确拒绝承诺?
代码
下面是我测试的代码和被测函数。
import { ConflictException } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { AuthCredentialsDto } from './dto/auth-credentials.dto';
import { UserRepository } from './user.repository';
describe('UserRepository', () => {
let userRepository: UserRepository;
let authCredentialsDto: AuthCredentialsDto = {
username: 'usahh',
password: 'passworD12!@',
};
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [UserRepository],
}).compile();
userRepository = module.get<UserRepository>(UserRepository);
});
describe('signUp', () => {
let save: any;
beforeEach(() => {
save = jest.fn();
userRepository.create = jest.fn().mockReturnValue({ save });
});
it('throws a conflict exception if user already exist', () => {
// My first try:
// save.mockRejectedValue({
// code: '23505',
// });
// Then I tried this, with and without async await:
save.mockImplementation(async () => {
await Promise.reject({ code: '23505' });
});
expect(userRepository.signUp(authCredentialsDto)).rejects.toThrow(
ConflictException,
);
});
});
});
这里测试的功能是:
@EntityRepository(User)
export class UserRepository extends Repository<User> {
async signUp(authCredentialsDto: AuthCredentialsDto): Promise<void> {
const { username, password } = authCredentialsDto;
const user = this.create();
user.salt = await bcrypt.genSalt();
user.username = username;
user.password = await this.hashPassword(password, user.salt);
try {
await user.save();
} catch (e) {
if (e.code === '23505') {
throw new ConflictException('Username already exists');
} else {
throw new InternalServerErrorException();
}
}
}
}
这应该是异步测试,但它是同步的,即使有被拒绝的承诺也不会失败。
需要链接 expect(...).rejects...
return 的承诺:
it('throws a conflict exception if user already exist', async () => {
...
await expect(userRepository.signUp(authCredentialsDto)).rejects.toThrow(
ConflictException,
);
});
mockImplementation
没有试错的余地。模拟应该 return 拒绝承诺,而 mockRejectedValue
这样做。 mockImplementation(async () => ...)
写得太长了。
我在做什么?
我正在学习 nestjs 课程,其中有一些单元测试。我编写了这个测试来检查存储库 class 中的 signUp 方法。问题是为了触发异常,行 user.save()
应该 return 承诺拒绝(模拟一些写入数据库的问题)。我尝试了几种方法(见下文),但 none 有效。
问题
结果是测试成功,但是有一个unhandled Promise rejection
。这样即使我断言 not.toThow()
它也会以相同的 unhandled Promise rejection
(node:10149) UnhandledPromiseRejectionWarning: Error: expect(received).rejects.toThrow()
Received promise resolved instead of rejected
Resolved to value: undefined
(Use `node --trace-warnings ...` to show where the warning was created)
如何让它正确拒绝承诺?
代码
下面是我测试的代码和被测函数。
import { ConflictException } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { AuthCredentialsDto } from './dto/auth-credentials.dto';
import { UserRepository } from './user.repository';
describe('UserRepository', () => {
let userRepository: UserRepository;
let authCredentialsDto: AuthCredentialsDto = {
username: 'usahh',
password: 'passworD12!@',
};
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [UserRepository],
}).compile();
userRepository = module.get<UserRepository>(UserRepository);
});
describe('signUp', () => {
let save: any;
beforeEach(() => {
save = jest.fn();
userRepository.create = jest.fn().mockReturnValue({ save });
});
it('throws a conflict exception if user already exist', () => {
// My first try:
// save.mockRejectedValue({
// code: '23505',
// });
// Then I tried this, with and without async await:
save.mockImplementation(async () => {
await Promise.reject({ code: '23505' });
});
expect(userRepository.signUp(authCredentialsDto)).rejects.toThrow(
ConflictException,
);
});
});
});
这里测试的功能是:
@EntityRepository(User)
export class UserRepository extends Repository<User> {
async signUp(authCredentialsDto: AuthCredentialsDto): Promise<void> {
const { username, password } = authCredentialsDto;
const user = this.create();
user.salt = await bcrypt.genSalt();
user.username = username;
user.password = await this.hashPassword(password, user.salt);
try {
await user.save();
} catch (e) {
if (e.code === '23505') {
throw new ConflictException('Username already exists');
} else {
throw new InternalServerErrorException();
}
}
}
}
这应该是异步测试,但它是同步的,即使有被拒绝的承诺也不会失败。
需要链接 expect(...).rejects...
return 的承诺:
it('throws a conflict exception if user already exist', async () => {
...
await expect(userRepository.signUp(authCredentialsDto)).rejects.toThrow(
ConflictException,
);
});
mockImplementation
没有试错的余地。模拟应该 return 拒绝承诺,而 mockRejectedValue
这样做。 mockImplementation(async () => ...)
写得太长了。