如何对 NestJS 中的控制器应用警卫进行单元测试?
Posted
技术标签:
【中文标题】如何对 NestJS 中的控制器应用警卫进行单元测试?【英文标题】:How can I unit test that a guard is applied on a controller in NestJS? 【发布时间】:2020-05-03 03:53:51 【问题描述】:我在 NestJS 中配置了一个控制器,我想检查是否设置了适当的保护 - 有没有人有一个如何做到的例子?
这个(删节的)示例作为应用程序可以正常工作,所以我只是在测试指导之后。
您会在用户测试中注意到我调用Reflect.getMetadata
的测试。我正在追求这样的东西 - 当我在 __guards__
元数据上检查它时,这是一个函数,我正在努力模拟它,所以我可以检查它是否在设置时与 AuthGuard('jwt')
一起应用。
用户.controller.ts
@Controller('/api/user')
export class UserController
@UseGuards(AuthGuard('jwt'))
@Get()
user(@Request() req)
return req.user;
User.controller.spec.ts
describe('User Controller', () =>
// beforeEach setup as per the cli generator
describe('#user', () =>
beforeEach(() =>
// This is how I'm checking the @Get() decorator is applied correctly - I'm after something for __guards__
expect(Reflect.getMetadata('path', controller.user)).toBe('/');
expect(Reflect.getMetadata('method', controller.user)).toBe(RequestMethod.GET);
);
it('should return the user', () =>
const req =
user: 'userObj',
;
expect(controller.user(req)).toBe(req.user);
);
);
);
【问题讨论】:
我曾经使用 e2e 进行此测试,但在我看来,它们绝对是完全有效的回归测试。 @RuslanGonzalez 是的,e2e 测试很重要。我认为 e2e 测试检查它们是否应用了正确的功能,而单元测试检查它们是否被应用——两者对于它们协同工作都很重要且至关重要。不过,单元测试往往更快 【参考方案1】:对于它的价值,您不需要测试框架提供的装饰器是否也设置了您所期望的。这就是为什么框架开始对它们进行测试的原因。不过,如果您想检查装饰器是否确实设置了预期的元数据you can see that done here。
如果您只是想测试守卫,您可以直接实例化 GuardClass 并通过提供 ExecutionContext
对象来测试其 canActivate
方法。 I've got an example here。该示例使用library that creates mock objects for you(从那时起为renamed),但它的想法是您将创建一个类似
const mockExecutionContext: Partial<
Record<
jest.FunctionPropertyNames<ExecutionContext>,
jest.MockedFunction<any>
>
> =
switchToHttp: jest.fn().mockReturnValue(
getRequest: jest.fn(),
getResponse: jest.fn(),
),
;
其中getRequest
和getResponse
返回HTTP 请求和响应对象(或至少它们的一部分)。要仅使用此对象,您还需要使用 as any
以防止 Typescript 抱怨太多。
【讨论】:
这不是我真正想做的事。我希望确保为该方法设置了保护装饰器。我不在乎装饰器在下面做了什么(出于你提到的原因),我已经围绕警卫本身进行了测试。如果这是一个“经典”的 Express 应用程序,我可以测试中间件是否应用于路由,这是我在这里尝试实现的,但使用装饰器 我不太确定我是否理解您当时想要实现的目标。第一个链接显示了显示元数据在类和类方法上正确设置的测试,这就是“设置”守卫的方式。如果你想测试当你调用路由时守卫被执行,那么你需要设置 supertest 来调用路由。也许我不明白你要做什么 是的,e2e 测试是实现这一目标的一种选择。这很可能是一种更合适的方式。我给出的示例确实检查了是否设置了正确的元数据,但那是因为我无法找到更好的方法来测试应用了 @Get 装饰器(我愿意接受建议)。我想要实现的是一个测试,以确保设置了适当的保护措施——我更喜欢通过单元测试而不是 e2e 来完成,因为它会出现在覆盖率报告中,但这不会破坏交易跨度> 看来元数据的反射仍然是你最好的选择。在上面的示例中,您可以进行类似expect(Reflect.getMetadata('__guards__', UserController.user)).toEqual(MixinAuthGuard)
的测试。 MixinAuthGuard 是 mixin AuthGuard('jwt')
产生(或应该产生)的类。这将断言应用于 UserController.user 方法的守卫(即 GET /api/user 路由)将是正确的守卫
另请参阅相关问题 (***.com/questions/62595603/…),那里有一个用于模拟 ExecutionContext
的最新示例。还值得一提——包@golevelup/nestjs-testing
已重命名为@golevelup/ts-jest
,见github.com/golevelup/nestjs/issues/265【参考方案2】:
我意识到它并不完全是您正在寻找的答案,但是在@Jay McDoniel 的答案的基础上,我使用以下内容来测试自定义装饰器在控制器功能上的存在(尽管我不能 100% 确定这是否正确测试非自定义守卫的方法)
import Controller from '@nestjs/common';
import UseGuards from '@nestjs/common';
import JwtAuthGuard from './jwtAuthGuard';
@Controller()
export class MyController
@UseGuards(JwtAuthGuard)
user()
...
it('should ensure the JwtAuthGuard is applied to the user method', async () =>
const guards = Reflect.getMetadata('__guards__', MyController.prototype.user)
const guard = new (guards[0])
expect(guard).toBeInstanceOf(JwtAuthGuard)
);
对于控制器
it('should ensure the JwtAuthGuard is applied to the controller', async () =>
const guards = Reflect.getMetadata('__guards__', MyController)
const guard = new (guards[0])
expect(guard).toBeInstanceOf(JwtAuthGuard)
);
【讨论】:
太好了,谢谢。正如我在 OP 中所说,我不是在测试装饰器的功能之后,只是测试了它们是否应用了适当的配置。由于我们在代码中依赖它,我认为这应该是单元测试的一部分以上是关于如何对 NestJS 中的控制器应用警卫进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
GraphQL + NestJS - 我如何在警卫中访问@Args?