Dan*_*iel 9 unit-testing dependency-injection mocking jestjs nestjs
我已经开始使用 NestJS 并且有一个关于模拟单元测试的守卫的问题。我正在尝试测试一个基本的 HTTP controller,它有一个方法 Guard 附加到它。
当我向 Guard 注入服务时,我的问题就开始了(我需要ConfigServiceGuard 的服务)。
运行测试时,DI 无法解析 Guard
? AppController › root › should return "Hello World!"
Nest can't resolve dependencies of the ForceFailGuard (?). Please make sure that the argument at index [0] is available in the _RootTestModule context.
Run Code Online (Sandbox Code Playgroud)
我的力量失败守卫:
? AppController › root › should return "Hello World!"
Nest can't resolve dependencies of the ForceFailGuard (?). Please make sure that the argument at index [0] is available in the _RootTestModule context.
Run Code Online (Sandbox Code Playgroud)
规范文件:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { ConfigService } from './config.service';
@Injectable()
export class ForceFailGuard implements CanActivate {
constructor(
private configService: ConfigService,
) {}
canActivate(context: ExecutionContext) {
return !this.configService.get().shouldFail;
}
}
Run Code Online (Sandbox Code Playgroud)
我无法找到有关此问题的示例或文档。我错过了什么还是这是一个真正的问题?
感谢任何帮助,谢谢。
小智 9
提供的示例存储库存在 3 个问题:
Nestjs v6.1.1 中存在一个错误.overrideGuard()- 请参阅https://github.com/nestjs/nest/issues/2070
我已经确认它已在 6.5.0 中修复。
ForceFailGuard在 中providers,但它的依赖项 ( ConfigService) 在 created 中不可用TestingModule。
如果您想模拟ForceFailGuard,只需将其从中删除providers并让其.overrideGuard()完成其工作。
mock_ForceFailGuard有CanActivate作为财产而不是canActivate.
工作示例(nestjs v6.5.0):
import { CanActivate } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ForceFailGuard } from './force-fail.guard';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const mock_ForceFailGuard: CanActivate = { canActivate: jest.fn(() => true) };
const app: TestingModule = await Test
.createTestingModule({
controllers: [AppController],
providers: [
AppService,
],
})
.overrideGuard(ForceFailGuard).useValue(mock_ForceFailGuard)
.compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
Run Code Online (Sandbox Code Playgroud)
如果除了控制器单元测试之外,您还需要/想要对自定义防护实现进行单元测试,您可以进行类似于下面的测试,以预期错误等
// InternalGuard.ts
@Injectable()
export class InternalTokenGuard implements CanActivate {
constructor(private readonly config: ConfigService) {
}
public async canActivate(context: ExecutionContext): Promise<boolean> {
const token = this.config.get("internalToken");
if (!token) {
throw new Error(`No internal token was provided.`);
}
const request = context.switchToHttp().getRequest();
const providedToken = request.headers["authorization"];
if (token !== providedToken) {
throw new UnauthorizedException();
}
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
还有你的规格文件
// InternalGuard.spec.ts
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [],
providers: [
InternalTokenGuard,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === "internalToken") {
return 123;
}
return null;
})
}
}
]
}).compile();
config = module.get<ConfigService>(ConfigService);
guard = module.get<InternalTokenGuard>(InternalTokenGuard);
});
it("should throw UnauthorizedException when token is not Bearer", async () => {
const context = {
getClass: jest.fn(),
getHandler: jest.fn(),
switchToHttp: jest.fn(() => ({
getRequest: jest.fn().mockReturnValue({
headers: {
authorization: "providedToken"
}
})
}))
} as any;
await expect(guard.canActivate(context)).rejects.toThrow(
UnauthorizedException
);
expect(context.switchToHttp).toHaveBeenCalled();
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4952 次 |
| 最近记录: |