Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
blencorp avatar

Route Tester

  • 51 installs
  • 101 repo stars
  • Updated November 28, 2025
  • blencorp/claude-code-kit

Route Tester is a Claude Code skill that provides framework-agnostic HTTP API route testing patterns, auth strategies, and integration testing best practices.

About

Route Tester is a skill that gives Claude framework-agnostic HTTP API route testing patterns, authentication strategies, and integration testing best practices. A developer uses it when writing unit, integration, or end-to-end tests for REST endpoints across Express, Next.js, FastAPI, Django REST, or Flask. It covers JWT cookie and bearer-token auth testing and per-HTTP-method assertions.

  • Framework-agnostic API route testing for Express, Next.js, FastAPI, Django REST, Flask
  • Auth testing for JWT cookie and bearer-token flows
  • Per-method (GET/POST/PATCH/DELETE) request and response assertions

Route Tester by the numbers

  • 51 all-time installs (skills.sh)
  • Ranked #1,222 of 2,153 Testing & QA skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
At a glance

route-tester capabilities & compatibility

Capabilities
testing · api development · code review
Use cases
testing · api development
IDEs
vscode · cursor ide
From the docs

What route-tester says it does

Framework-agnostic HTTP API route testing patterns, authentication strategies, and integration testing best practices.
SKILL.md
This skill provides framework-agnostic guidance for testing HTTP API routes and endpoints across any backend framework (Express, Next.js API Routes, FastAPI, Django REST, Flask, etc.).
SKILL.md
Fast execution (< 50ms per test)
SKILL.md
npx skills add https://github.com/blencorp/claude-code-kit --skill route-tester

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs51
repo stars101
Last updatedNovember 28, 2025
Repositoryblencorp/claude-code-kit

What it does

Write unit, integration, and end-to-end tests for HTTP API routes with auth flows.

Who is it for?

Writing tests for REST API routes with authentication

Skip if: Frontend UI testing or non-HTTP code

When should I use this skill?

Testing HTTP API routes and endpoints across any backend framework

What you get

API routes have unit, integration, and end-to-end tests covering auth and each HTTP method.

  • Unit tests
  • Integration tests
  • End-to-end tests

By the numbers

  • 3 test types: unit, integration, end-to-end
  • unit tests under 50ms per test

Files

SKILL.mdMarkdownGitHub ↗

API Route Testing Skill

This skill provides framework-agnostic guidance for testing HTTP API routes and endpoints across any backend framework (Express, Next.js API Routes, FastAPI, Django REST, Flask, etc.).

Core Testing Principles

1. Test Types for API Routes

Unit Tests

  • Test individual route handlers in isolation
  • Mock dependencies (database, external APIs)
  • Fast execution (< 50ms per test)
  • Focus on business logic

Integration Tests

  • Test full request/response cycle
  • Real database (test instance)
  • Authentication flow included
  • Slower but more comprehensive

End-to-End Tests

  • Test from client perspective
  • Full authentication flow
  • Real services (or close replicas)
  • Most realistic, slowest execution

2. Authentication Testing Patterns

JWT Cookie Authentication
// Common pattern across frameworks
describe('Protected Route Tests', () => {
  let authCookie: string;

  beforeEach(async () => {
    // Login and get JWT cookie
    const loginResponse = await request(app)
      .post('/api/auth/login')
      .send({ email: 'test@example.com', password: 'password123' });

    authCookie = loginResponse.headers['set-cookie'][0];
  });

  it('should access protected route with valid cookie', async () => {
    const response = await request(app)
      .get('/api/protected/resource')
      .set('Cookie', authCookie);

    expect(response.status).toBe(200);
  });

  it('should reject access without cookie', async () => {
    const response = await request(app)
      .get('/api/protected/resource');

    expect(response.status).toBe(401);
  });
});
JWT Bearer Token Authentication
describe('Bearer Token Auth', () => {
  let token: string;

  beforeEach(async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({ email: 'test@example.com', password: 'password123' });

    token = response.body.token;
  });

  it('should authenticate with bearer token', async () => {
    const response = await request(app)
      .get('/api/protected/resource')
      .set('Authorization', `Bearer ${token}`);

    expect(response.status).toBe(200);
  });
});

3. HTTP Method Testing

GET Requests

describe('GET /api/users', () => {
  it('should return paginated users', async () => {
    const response = await request(app)
      .get('/api/users?page=1&limit=10');

    expect(response.status).toBe(200);
    expect(response.body).toHaveProperty('data');
    expect(response.body).toHaveProperty('pagination');
    expect(Array.isArray(response.body.data)).toBe(true);
  });

  it('should filter users by query params', async () => {
    const response = await request(app)
      .get('/api/users?role=admin');

    expect(response.status).toBe(200);
    expect(response.body.data.every(u => u.role === 'admin')).toBe(true);
  });
});

POST Requests

describe('POST /api/users', () => {
  it('should create new user with valid data', async () => {
    const newUser = {
      name: 'John Doe',
      email: 'john@example.com',
      role: 'user'
    };

    const response = await request(app)
      .post('/api/users')
      .set('Cookie', authCookie)
      .send(newUser);

    expect(response.status).toBe(201);
    expect(response.body).toMatchObject(newUser);
    expect(response.body).toHaveProperty('id');
  });

  it('should reject invalid data', async () => {
    const invalidUser = {
      name: 'John Doe'
      // Missing required email field
    };

    const response = await request(app)
      .post('/api/users')
      .set('Cookie', authCookie)
      .send(invalidUser);

    expect(response.status).toBe(400);
    expect(response.body).toHaveProperty('errors');
  });
});

PUT/PATCH Requests

describe('PATCH /api/users/:id', () => {
  it('should update user fields', async () => {
    const updates = { name: 'Jane Doe' };

    const response = await request(app)
      .patch('/api/users/123')
      .set('Cookie', authCookie)
      .send(updates);

    expect(response.status).toBe(200);
    expect(response.body.name).toBe('Jane Doe');
  });

  it('should return 404 for non-existent user', async () => {
    const response = await request(app)
      .patch('/api/users/999999')
      .set('Cookie', authCookie)
      .send({ name: 'Test' });

    expect(response.status).toBe(404);
  });
});

DELETE Requests

describe('DELETE /api/users/:id', () => {
  it('should delete user and return success', async () => {
    const response = await request(app)
      .delete('/api/users/123')
      .set('Cookie', authCookie);

    expect(response.status).toBe(204);
  });

  it('should prevent unauthorized deletion', async () => {
    const response = await request(app)
      .delete('/api/users/123');
      // No auth cookie

    expect(response.status).toBe(401);
  });
});

4. Response Validation

Status Codes

describe('HTTP Status Codes', () => {
  it('200 OK - Successful GET', async () => {
    const response = await request(app).get('/api/users');
    expect(response.status).toBe(200);
  });

  it('201 Created - Successful POST', async () => {
    const response = await request(app).post('/api/users').send(validData);
    expect(response.status).toBe(201);
  });

  it('204 No Content - Successful DELETE', async () => {
    const response = await request(app).delete('/api/users/123');
    expect(response.status).toBe(204);
  });

  it('400 Bad Request - Invalid input', async () => {
    const response = await request(app).post('/api/users').send({});
    expect(response.status).toBe(400);
  });

  it('401 Unauthorized - Missing auth', async () => {
    const response = await request(app).get('/api/protected');
    expect(response.status).toBe(401);
  });

  it('403 Forbidden - Insufficient permissions', async () => {
    const response = await request(app).delete('/api/admin/users/123').set('Cookie', userCookie);
    expect(response.status).toBe(403);
  });

  it('404 Not Found - Non-existent resource', async () => {
    const response = await request(app).get('/api/users/999999');
    expect(response.status).toBe(404);
  });

  it('500 Internal Server Error - Server failure', async () => {
    // Test error handling
    mockDatabase.findOne.mockRejectedValue(new Error('DB Error'));
    const response = await request(app).get('/api/users/123');
    expect(response.status).toBe(500);
  });
});

Response Schema Validation

describe('Response Schema', () => {
  it('should match expected schema', async () => {
    const response = await request(app).get('/api/users/123');

    expect(response.body).toEqual({
      id: expect.any(String),
      name: expect.any(String),
      email: expect.any(String),
      role: expect.stringMatching(/^(user|admin)$/),
      createdAt: expect.any(String),
      updatedAt: expect.any(String)
    });
  });
});

5. Error Handling Tests

describe('Error Handling', () => {
  it('should return structured error response', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ invalid: 'data' });

    expect(response.status).toBe(400);
    expect(response.body).toEqual({
      error: expect.any(String),
      message: expect.any(String),
      errors: expect.any(Array)
    });
  });

  it('should handle database errors gracefully', async () => {
    mockDatabase.findOne.mockRejectedValue(new Error('Connection lost'));

    const response = await request(app).get('/api/users/123');

    expect(response.status).toBe(500);
    expect(response.body.error).toBe('Internal Server Error');
  });

  it('should sanitize error messages in production', async () => {
    process.env.NODE_ENV = 'production';

    const response = await request(app).get('/api/error-prone-route');

    expect(response.status).toBe(500);
    expect(response.body.message).not.toContain('stack trace');
    expect(response.body.message).not.toContain('SQL');
  });
});

6. Test Setup and Teardown

describe('API Tests', () => {
  let testDatabase;

  beforeAll(async () => {
    // Initialize test database
    testDatabase = await initTestDatabase();
  });

  afterAll(async () => {
    // Clean up test database
    await testDatabase.close();
  });

  beforeEach(async () => {
    // Seed test data
    await testDatabase.seed();
  });

  afterEach(async () => {
    // Clear test data
    await testDatabase.clear();
  });

  // Tests...
});

Framework-Specific Testing Libraries

While this skill provides framework-agnostic patterns, here are common testing libraries per framework:

  • Express: supertest, jest, vitest
  • Next.js API Routes: @testing-library/react, next-test-api-route-handler
  • FastAPI: pytest, httpx
  • Django REST: django.test.TestCase, rest_framework.test
  • Flask: pytest, flask.testing

Best Practices

1. Use descriptive test names - Test names should describe the scenario and expected outcome 2. Test happy path and edge cases - Cover both success and failure scenarios 3. Isolate tests - Each test should be independent and not rely on other tests 4. Use realistic test data - Test data should mimic production data 5. Clean up after tests - Always reset state between tests 6. Mock external dependencies - Don't call real external APIs in tests 7. Test authentication edge cases - Expired tokens, invalid tokens, missing tokens 8. Validate response schemas - Ensure APIs return expected structure 9. Test rate limiting - Verify rate limits work correctly 10. Test CORS headers - Ensure CORS is configured correctly

Common Pitfalls

Don't share state between tests

// Bad
let userId;
it('creates user', async () => {
  const response = await request(app).post('/api/users').send(userData);
  userId = response.body.id; // Shared state!
});

it('deletes user', async () => {
  await request(app).delete(`/api/users/${userId}`); // Depends on previous test
});

Do create fresh state for each test

// Good
it('creates user', async () => {
  const response = await request(app).post('/api/users').send(userData);
  expect(response.status).toBe(201);
});

it('deletes user', async () => {
  const user = await createTestUser();
  const response = await request(app).delete(`/api/users/${user.id}`);
  expect(response.status).toBe(204);
});

Additional Resources

See the resources/ directory for more detailed guides:

  • http-testing-fundamentals.md - Deep dive into HTTP testing concepts
  • authentication-testing.md - Authentication strategies and edge cases
  • api-integration-testing.md - Integration testing patterns and tools

Quick Reference

Test Structure

describe('Resource Name', () => {
  describe('HTTP Method /path', () => {
    it('should describe expected behavior', async () => {
      // Arrange
      const testData = {...};

      // Act
      const response = await request(app)
        .method('/path')
        .set('Cookie', authCookie)
        .send(testData);

      // Assert
      expect(response.status).toBe(expectedStatus);
      expect(response.body).toMatchObject(expectedData);
    });
  });
});

Authentication Pattern

let authCookie: string;

beforeEach(async () => {
  const response = await request(app)
    .post('/api/auth/login')
    .send({ email: 'test@example.com', password: 'password123' });

  authCookie = response.headers['set-cookie'][0];
});

// Use authCookie in protected route tests
.set('Cookie', authCookie)

Related skills

FAQ

Is this tied to one framework?

No, it is framework-agnostic and applies to Express, Next.js API Routes, FastAPI, Django REST, and Flask.

What auth patterns are covered?

JWT cookie authentication and JWT bearer-token authentication.

Testing & QAtestingbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.