Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Nextjs Testing

ASecurity

Testing strategies for Next.js: Server Components, Client Components, Server Actions, Route Handlers, end-to-end with Playwright. Vitest/Jest configuration, React Testing Library patterns, msw for network mocks. Use this skill to: - Pick the right test layer for what you're testing. - Configure Vitest or Jest for Next.js. - Test Server Components without running them in isolation. - Test Server Actions and Route Handlers as pure functions. - Set up Playwright for e2e and integration of RSC. ...

35 stars
0 votes
0 copies
0 views
Added 9/22/2026
testinggoreactnextjsnodenodejstestingapidatabase

Works with

cliapi

Security Analysis

A92/100
mediumInstalls packages at runtime which could introduce malicious dependencies

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add AratKruglik/claude-sdlc --skill nextjs-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nextjs Testing?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Nextjs Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aratkruglik-nextjs-testing/badge)](https://www.skillsdirectory.com/skills/aratkruglik-nextjs-testing)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: nextjs-testing
description: |
  Testing strategies for Next.js: Server Components, Client Components, Server Actions, Route Handlers, end-to-end with Playwright. Vitest/Jest configuration, React Testing Library patterns, msw for network mocks.

  Use this skill to:
  - Pick the right test layer for what you're testing.
  - Configure Vitest or Jest for Next.js.
  - Test Server Components without running them in isolation.
  - Test Server Actions and Route Handlers as pure functions.
  - Set up Playwright for e2e and integration of RSC.

  Do NOT use this skill for:
  - General Next.js conventions (see nextjs-conventions).
  - RSC vs Client model (see server-component-patterns).
  - Plain Node.js test patterns (see nodejs-plugin equivalents).
user-invocable: false
paths: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}", "e2e/**"]
---

# Next.js Testing Patterns

Next.js testing splits along the same line as the runtime: server-side code runs in Node, client-side in jsdom or a browser. The right tool depends on the layer.

## Test framework selection

| Layer | Framework |
|---|---|
| Client Components, hooks, plain JS/TS units | **Vitest** (preferred for new projects) or **Jest** |
| Server Components | **Playwright** integration tests OR Vitest with the right config |
| Server Actions | **Vitest** (treat the action body as a pure function — extract logic) |
| Route Handlers | **Vitest** with mocked `NextRequest` |
| End-to-end (full stack) | **Playwright** (Vercel-recommended) or **Cypress** |

For new projects, prefer Vitest + Playwright. Match what exists otherwise.

## Vitest setup for Next.js

`vitest.config.ts`:

```ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    setupFiles: ['./vitest.setup.ts'],
    globals: true,
    css: true,
  },
  resolve: {
    alias: { '@': path.resolve(__dirname, './') },
  },
});
```

`vitest.setup.ts`:

```ts
import '@testing-library/jest-dom/vitest';
```

Install: `pnpm add -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event`.

## Testing Client Components (RTL)

```tsx
// app/users/_components/UserFilter.test.tsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserFilter } from './UserFilter';

describe('UserFilter', () => {
  it('calls onChange as user types', async () => {
    const onChange = vi.fn();
    const user = userEvent.setup();
    render(<UserFilter onChange={onChange} />);

    await user.type(screen.getByRole('textbox'), 'alice');

    expect(onChange).toHaveBeenLastCalledWith('alice');
  });
});
```

Query priority: `getByRole` > `getByLabelText` > `getByText` > `getByTestId` (last resort). Roles match how assistive tech sees the UI.

## Testing Server Components

Server Components are async and run on the server — they don't render in jsdom directly. Two approaches:

### Approach A — Extract data logic, test that

```ts
// app/users/page.tsx
import { db } from '@/lib/db';
import { UserList } from './_components/UserList';

export async function getUsers() {
  return db.users.findMany();
}

export default async function UsersPage() {
  const users = await getUsers();
  return <UserList users={users} />;
}

// app/users/page.test.ts
import { describe, it, expect, vi } from 'vitest';
import { getUsers } from './page';
import { db } from '@/lib/db';

vi.mock('@/lib/db', () => ({
  db: { users: { findMany: vi.fn().mockResolvedValue([{ id: '1', name: 'Alice' }]) } },
}));

describe('getUsers', () => {
  it('returns users from DB', async () => {
    expect(await getUsers()).toEqual([{ id: '1', name: 'Alice' }]);
  });
});
```

The view layer (`UserList`) is a pure component — test separately as a unit.

### Approach B — Playwright integration

```ts
// e2e/users.spec.ts
import { test, expect } from '@playwright/test';

test('users page lists users', async ({ page }) => {
  await page.goto('/users');
  await expect(page.getByRole('listitem')).toHaveCount(3);
  await expect(page.getByText('Alice')).toBeVisible();
});
```

Run against a test database with known seed data, OR mock the API layer the page consumes.

## Testing Server Actions

Server Actions are async functions — testable as pure code if you extract the logic:

```ts
// app/users/actions.ts
'use server';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';

const Schema = z.object({ email: z.string().email(), name: z.string().min(1) });

export async function createUser(formData: FormData) {
  const parsed = Schema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { ok: false as const, error: parsed.error.flatten() };
  const user = await db.users.create({ data: parsed.data });
  revalidatePath('/users');
  return { ok: true as const, user };
}
```

```ts
// app/users/actions.test.ts
import { describe, it, expect, vi } from 'vitest';
import { createUser } from './actions';

vi.mock('@/lib/db', () => ({
  db: { users: { create: vi.fn().mockResolvedValue({ id: '1', email: 'a@b.c', name: 'A' }) } },
}));
vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }));

describe('createUser', () => {
  it('returns ok with valid input', async () => {
    const fd = new FormData();
    fd.set('email', 'a@b.c');
    fd.set('name', 'Alice');
    const result = await createUser(fd);
    expect(result.ok).toBe(true);
  });

  it('returns error on invalid input', async () => {
    const fd = new FormData();
    fd.set('email', 'invalid');
    const result = await createUser(fd);
    expect(result.ok).toBe(false);
  });
});
```

For the auth check at the top of the action, mock `auth()` similarly.

## Testing Route Handlers

```ts
// app/api/users/route.test.ts
import { describe, it, expect, vi } from 'vitest';
import { NextRequest } from 'next/server';
import { GET, POST } from './route';

vi.mock('@/lib/db', () => ({
  db: { users: { findMany: vi.fn().mockResolvedValue([]), create: vi.fn() } },
}));

describe('GET /api/users', () => {
  it('returns 200 with users', async () => {
    const res = await GET();
    expect(res.status).toBe(200);
    expect(await res.json()).toEqual([]);
  });
});

describe('POST /api/users', () => {
  it('returns 400 on invalid body', async () => {
    const req = new NextRequest('http://localhost/api/users', {
      method: 'POST',
      body: JSON.stringify({ email: 'bad' }),
    });
    const res = await POST(req);
    expect(res.status).toBe(400);
  });
});
```

For dynamic routes:

```ts
import { GET } from './[id]/route';
const res = await GET(new NextRequest('...'), { params: Promise.resolve({ id: '123' }) });
```

(In Next.js 15+ params is Promise; otherwise plain object.)

## Network mocking with msw

For component tests that hit `fetch`:

```ts
// vitest.setup.ts
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';

const server = setupServer(
  http.get('https://api.example.com/users', () =>
    HttpResponse.json([{ id: '1', name: 'Alice' }])
  )
);

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
```

Per-test override:

```ts
import { http, HttpResponse } from 'msw';
server.use(http.get('...', () => HttpResponse.error()));
```

## Playwright setup

`playwright.config.ts`:

```ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { browserName: 'chromium' } },
    { name: 'firefox', use: { browserName: 'firefox' } },
    { name: 'webkit', use: { browserName: 'webkit' } },
  ],
});
```

Install: `pnpm dlx playwright install`.

## Coverage discipline

Target ≥80% on services, route handlers, server actions, and Client Components with logic. Skip:

- Pure presentational components (snapshot churn outweighs value).
- Layouts (mostly composition).
- `next.config.js`, `middleware.ts` config bodies (test the behavior end-to-end).

Configure exclusions:

```ts
// vitest.config.ts
test: {
  coverage: {
    exclude: [
      'app/**/layout.tsx',
      'app/**/loading.tsx',
      'app/**/error.tsx',
      'app/**/not-found.tsx',
      '**/*.config.{js,ts,mjs}',
      'middleware.ts',
    ],
  },
}
```

## Iteration cap (from QA agent)

The qa-engineer agent has a hard 3-attempt cap on fixing failing tests. If a test is fundamentally fragile after 3 attempts, mark it `it.skip(...)` with a comment explaining why and report in the QA summary. Don't iterate past the cap.

## Anti-patterns

- ❌ Trying to render a Server Component in jsdom directly. Extract data logic, test that; integration-test the rendered HTML via Playwright.
- ❌ Asserting on implementation details (CSS classes, internal state). Assert on user-visible behavior.
- ❌ `getByTestId` as the first choice. Use `getByRole` / `getByLabelText` first.
- ❌ Real network calls in unit tests. Use msw or vi.mock at the module boundary.
- ❌ Snapshot tests of large component trees. They turn into review noise.
- ❌ E2E tests against `localhost:3000` without `webServer` config — Playwright won't start the dev server.
- ❌ Mocking the SUT instead of its dependencies.

Attribution

AratKruglikAratKruglik
View sourceMore from AratKruglik →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Screen Reader Testing

Practical guide to testing web applications with screen readers for comprehensive accessibility validation.

397921 votes

Tdd Workflow

在编写新功能、修复错误或重构代码时使用此技能。强制执行测试驱动开发,包含单元测试、集成测试和端到端测试,覆盖率超过80%。

2456590 votes

Python Testing

使用pytest、TDD方法、夹具、模拟、参数化和覆盖率要求的Python测试策略。

2456590 votes

Springboot Tdd

使用JUnit 5、Mockito、MockMvc、Testcontainers和JaCoCo进行Spring Boot的测试驱动开发。适用于添加功能、修复错误或重构时。

2456590 votes

Golang Testing

Go测试模式包括表格驱动测试、子测试、基准测试、模糊测试和测试覆盖率。遵循TDD方法论,采用地道的Go实践。

2456590 votes
View all in testing →