Careeroza
Testing Guards and Controllers
A Guard Unit Test
Guards take an ExecutionContext; build a small fake one.
src/auth/roles.guard.spec.ts
import { ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { RolesGuard } from './roles.guard';
const contextFor = (user: unknown): ExecutionContext =>
({
getHandler: () => () => undefined,
getClass: () => class {},
switchToHttp: () => ({ getRequest: () => ({ user }) }),
}) as unknown as ExecutionContext;
describe('RolesGuard', () => {
const reflector = { getAllAndOverride: jest.fn() } as unknown as Reflector;
const guard = new RolesGuard(reflector);
it('allows when no roles are required', () => {
(reflector.getAllAndOverride as jest.Mock).mockReturnValue(undefined);
expect(guard.canActivate(contextFor({ roles: ['customer'] }))).toBe(true);
});
it('blocks a customer on an admin route', () => {
(reflector.getAllAndOverride as jest.Mock).mockReturnValue(['admin']);
expect(guard.canActivate(contextFor({ roles: ['customer'] }))).toBe(false);
});
});A Controller Unit Test
describe('ProductsController', () => {
let controller: ProductsController;
const service = { findAll: jest.fn().mockResolvedValue([{ id: 7 }]) };
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [ProductsController],
providers: [{ provide: ProductsService, useValue: service }],
}).compile();
controller = module.get(ProductsController);
});
it('delegates to the service', async () => {
await expect(controller.findAll('audio')).resolves.toEqual([{ id: 7 }]);
expect(service.findAll).toHaveBeenCalledWith('audio');
});
});Tip: Controller unit tests do not run pipes, guards or interceptors. Test those behaviours (400 on bad body, 401 without a token) in e2e tests.