Testing
Testing authentication is annoying. You need valid tokens, but you don't want to create real users or hit AWS in every test.
The testing package solves this. It can generate fake tokens for fast tests, or create real Cognito users for E2E tests.
Installation
pnpm add --save-dev @nestjs-cognito/testing # NestJS Cognito testing utilities
pnpm add --save-dev @nestjs/testing # NestJS testing utilities
pnpm add --save-dev jest # Test runner
pnpm add --save-dev @types/jest # TypeScript definitions
Real E2E tests with Cognito
This creates real users in AWS Cognito. Use this to test your authentication flow end-to-end.
import { handler, request, spec } from "pactum";
import { Test } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
describe('Auth Tests', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
// Configure Pactum to use the NestJS application
request.setBaseUrl(`http://localhost:${process.env.PORT || 3000}`);
});
afterAll(async () => {
await app.close();
});
});
Setup:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { CognitoTestingModule } from '@nestjs-cognito/testing';
import { CognitoAuthModule } from '@nestjs-cognito/auth';
@Module({
imports: [
// Configure auth module with environment variables
CognitoAuthModule.registerAsync({
imports: [ConfigModule.forRoot()],
useFactory: (config: ConfigService) => ({
jwtVerifier: {
userPoolId: config.get('COGNITO_USER_POOL_ID'),
clientId: config.get('COGNITO_CLIENT_ID'),
tokenUse: 'id',
},
}),
inject: [ConfigService],
}),
// Configure testing module with AWS credentials
CognitoTestingModule.registerAsync({
imports: [ConfigModule.forRoot()],
useFactory: (config: ConfigService) => ({
identityProvider: {
region: config.get('COGNITO_REGION'),
},
}),
inject: [ConfigService],
}),
],
})
export class TestModule {}
Fast tests with mocks
Skip AWS completely. The mock mode generates fake tokens that pass verification.
import { COGNITO_JWT_VERIFIER_INSTANCE_TOKEN } from '@nestjs-cognito/core';
import { CognitoTestingModule } from '@nestjs-cognito/testing';
import { Test } from "@nestjs/testing";
import { handler, request, spec } from "pactum";
describe('Auth Tests', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
CognitoTestingModule.register({}, {
enabled: true, // Enable mock mode
user: {
username: 'test-user',
email: 'test@example.com',
groups: ['users'],
},
}),
AppModule,
],
})
.overrideProvider(COGNITO_JWT_VERIFIER_INSTANCE_TOKEN)
.useFactory({
factory: CognitoTestingModule.createJwtVerifierFactory
})
.compile();
app = moduleFixture.createNestApplication();
await app.init();
});
});
Test protected routes
// Get a fake token
const response = await request(app.getHttpServer())
.post('/cognito-testing-login')
.send({
username: 'test-user',
password: 'any-password', // Not validated in mock mode
clientId: 'mock-client-id'
});
// Use it to access protected routes
await request(app.getHttpServer())
.get('/protected')
.set('Authorization', `Bearer ${response.body.AccessToken}`)
.expect(200);
Test authorization
// Configure mock user with specific groups
await request(app.getHttpServer())
.post('/config')
.send({
enabled: true,
user: {
username: 'admin-user',
email: 'admin@example.com',
groups: ['admin'],
},
});
// Login
const loginResponse = await request(app.getHttpServer())
.post('/cognito-testing-login')
.send({
username: 'admin@example.com',
password: 'password',
clientId: 'test-client',
});
// Test protected route access
await request(app.getHttpServer())
.get('/admin/dashboard')
.set('Authorization', `Bearer ${loginResponse.body.AccessToken}`)
.expect(200);
});
});
Advanced Testing
Dynamic User Configuration
Update mock settings during test execution:
await request(app.getHttpServer())
.post('/config')
.send({
user: {
username: 'new-user',
email: 'new@example.com',
groups: ['admin'],
},
})
.expect(200);
Testing Different Token Types
describe('Token Type Tests', () => {
it('should validate ID tokens', async () => {
const response = await request(app.getHttpServer())
.post('/cognito-testing-login')
.send({
username: 'test@example.com',
password: 'password',
clientId: 'test-client',
})
.expect(200);
await request(app.getHttpServer())
.get('/protected')
.set('Authorization', `Bearer ${response.body.IdToken}`)
.expect(200);
});
it('should validate access tokens', async () => {
const response = await request(app.getHttpServer())
.post('/cognito-testing-login')
.send({
username: 'test@example.com',
password: 'password',
clientId: 'test-client',
})
.expect(200);
await request(app.getHttpServer())
.get('/protected')
.set('Authorization', `Bearer ${response.body.AccessToken}`)
.expect(200);
});
});