All skills
bmad-labs avatar

/typescript-e2e-testing

@3bcc7a6
by bmad-labsbmad-labs/skills15 stars
4

E2E and integration testing for TypeScript/NestJS projects using Jest, supertest, and real infrastructure via Docker (Kafka, PostgreSQL, MongoDB, Redis) with the Given-When-Then pattern. Use whenever the user is working on `.e2e-spec.ts` files or anything under `test/e2e/`, or asks to set up, write, review, run, debug, or optimize E2E or integration tests — including flaky tests, docker-compose for tests, Kafka/Redpanda consumers, test isolation, or GWT compliance.

Use this Skill: https://skilld.dev/gh/bmad-labs/skills/typescript-e2e-testing

This session only. Nothing lands on disk.

referencesredisrules.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Redis E2E Testing Rules

Core Rules

Rule Requirement
Port Use different port from dev (e.g., 6380)
Cleanup FLUSHDB in beforeEach
Connection Single connection for tests
TTL Verify TTL is set correctly
Close Properly close connections in afterAll

Cleanup Rules

FLUSHDB (Recommended)

beforeEach(async () => {
  await redis.flushdb();
});

Clear by Pattern

beforeEach(async () => {
  const keys = await redis.keys('test:*');
  if (keys.length > 0) {
    await redis.del(...keys);
  }
});

Different Database per Suite

// Each test suite uses different DB index (0-15)
const dbIndex = parseInt(process.env.JEST_WORKER_ID || '0', 10) % 16;
const redis = new Redis({ db: dbIndex, ...otherConfig });

Connection Rules

// ioredis config for tests
const redis = new Redis({
  host: 'localhost',
  port: 6380,
  db: 0,
  lazyConnect: true,
  retryDelayOnClusterDown: 100,
  retryDelayOnFailover: 100,
});

// Properly close in afterAll
afterAll(async () => {
  await redis.quit();  // Graceful close
});

TTL Assertion Rules

Verify TTL is Set

// Assert TTL within range (accounting for timing)
async function assertKeyTtl(key: string, minTtl: number, maxTtl: number): Promise<void> {
  const ttl = await redis.ttl(key);
  expect(ttl).toBeGreaterThanOrEqual(minTtl);
  expect(ttl).toBeLessThanOrEqual(maxTtl);
}

// Usage: Verify 1 hour TTL (allowing for test execution time)
await assertKeyTtl('user:profile:123', 3500, 3600);

TTL Units

// setex uses SECONDS
await redis.setex(key, 3600, value);  // 1 hour

// psetex uses MILLISECONDS
await redis.psetex(key, 3600000, value);  // 1 hour

// COMMON MISTAKE: Wrong unit
await redis.setex(key, 3600000, value);  // Wrong! This is 41 days

Pub/Sub Rules

Wait for Subscriber

// WRONG: Subscribe and publish immediately
subscriber.subscribe('channel');
await redis.publish('channel', message);  // May be missed!

// CORRECT: Wait for subscription confirmation
await new Promise<void>((resolve) => {
  subscriber.once('subscribe', () => resolve());
  subscriber.subscribe('channel');
});
await redis.publish('channel', message);

Clean Up Listeners

beforeEach(() => {
  receivedMessages.length = 0;
});

afterEach(() => {
  subscriber.removeAllListeners('message');
});

afterAll(async () => {
  await subscriber.unsubscribe();
  await subscriber.quit();
});

Assertion Rules

Assert Key Exists

// GOOD: Assert specific value
await helper.assertKeyExists('user:123', { name: 'Test', email: 'test@example.com' });

// BAD: Only check existence
const exists = await redis.exists('user:123');
expect(exists).toBe(1);

Assert Key Not Exists

await helper.assertKeyNotExists('cache:invalidated-key');

Performance Rules

Use Pipeline for Multiple Operations

// SLOW: Individual commands
await redis.set('key1', 'value1');
await redis.set('key2', 'value2');
await redis.set('key3', 'value3');

// FAST: Pipeline
const pipeline = redis.pipeline();
pipeline.set('key1', 'value1');
pipeline.set('key2', 'value2');
pipeline.set('key3', 'value3');
await pipeline.exec();

Use MGET/MSET for Batch Operations

// SLOW: Individual gets
const values = [];
for (const key of keys) {
  values.push(await redis.get(key));
}

// FAST: MGET
const values = await redis.mget(...keys);

// FAST: MSET
await redis.mset('key1', 'val1', 'key2', 'val2', 'key3', 'val3');

Error Handling Rules

Connection Retry

async function waitForRedis(redis: Redis, maxRetries = 10): Promise<void> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await redis.ping();
      return;
    } catch {
      await new Promise(r => setTimeout(r, 1000));
    }
  }
  throw new Error('Redis not available');
}

Checklist

Setup:

  • Different port from dev (e.g., 6380)
  • Persistence disabled (save "", appendonly no)
  • Memory limit set (maxmemory 100mb)

Cleanup:

  • FLUSHDB in beforeEach
  • Remove message listeners in afterEach
  • Close connections in afterAll

Assertions:

  • Verify key values, not just existence
  • Verify TTL within expected range
  • Use correct TTL units (seconds vs milliseconds)

Pub/Sub:

  • Wait for subscription confirmation before publishing
  • Clear message buffers between tests
  • Remove listeners after each test

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides comprehensive workflows and reference materials for E2E testing in TypeScript/NestJS projects. It uses standard development tools like Jest, Docker, and shell commands. No malicious patterns or security risks were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    30/103 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 3bcc7a6. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 5 months ago
  • TypeScript
  • jest
  • nestjs
  • e2e-testing
  • supertest
  • docker
  • kafka
  • postgres
  • mongodb
  • redis
  • integration-testing
  • gwt-pattern

README badge

README badge for bmad-labs/skills

Writes E2E and integration tests for NestJS projects using Jest and supertest against real Docker infrastructure (Kafka, PostgreSQL, MongoDB, Redis) following the Given-When-Then pattern. Covers test setup, writing, review, debugging, and optimization workflows with technology-specific helpers and isolation strategies for each service.

Generated from the current SKILL.md.

Does this skill work with NestJS only?
The skill is built for NestJS projects using Jest and supertest, but the patterns and workflows apply to any TypeScript backend with HTTP endpoints. Technology-specific helpers exist for Kafka, PostgreSQL, MongoDB, and Redis.
Do I need Docker running to use this skill?
Yes. The skill assumes real infrastructure via Docker (Kafka, PostgreSQL, MongoDB, Redis). Tests execute against actual services, not mocks.
What if my E2E tests are flaky or failing?
The Debugging E2E Test workflow provides a systematic protocol: fix one test at a time using isolated test runs, verify with 3-5 consecutive runs before moving to the next failure, then run the full suite only once all tests pass individually.
Does this skill cover GraphQL or gRPC testing?
Yes. The API testing reference includes examples for REST, GraphQL, and gRPC, plus external API mocking with MSW and Nock.
What is the Given-When-Then pattern and is it mandatory?
GWT is the mandatory test structure: Given describes setup state, When describes the action under test, Then describes expected outcomes. All E2E tests in this skill must follow this pattern.

Generated from the current SKILL.md. These answers refresh after source changes.