Token导航 LogoToken导航

Agent Skill · 前端设计

javascript-testing-patterns(JavaScript 测试模式)

用于辅助 Java 项目开发、面向对象设计、Spring 生态、Maven 或 Gradle 依赖和后端工程实践。它适合让 Agent 分析类结构、设计接口、整理服务分层、生成测试或检查常见代码坏味道。使用时需要结合项目已有架构、包结构和依赖版本,不应只按通用教程改代码;涉及数据库、事务、并发或框架配置时,应先确认运行环境和回归测试范围。

javascript-testing-patterns 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 8,386。

总安装 291周安装 12GitHub Stars 4下载 95核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:javascript-testing-patterns(JavaScript 测试模式)
来源仓库:https://github.com/futuregerald/futuregerald-claude-plugin
仓库路径:skills/javascript-testing-patterns
安装命令:
npx skills add https://github.com/futuregerald/futuregerald-claude-plugin --skill javascript-testing-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/futuregerald/futuregerald-claude-plugin --skill javascript-testing-patterns

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

用于辅助 Java 项目开发、类结构和接口设计。适合分析服务分层、生成测试或检查代码坏味道。使用时需结合项目已有架构和依赖版本。javascript-testing-patterns 属于前端设计类 Skill,可作为该场景下的辅助能力补充。不应只按通用教程改代码,应先确认运行环境。

涉及数据库、事务或并发时应优先回归测试。

来源仓库内路径:skills/javascript-testing-patterns

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/futuregerald/futuregerald-claude-plugin --skil…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

javascript-testing-patterns 怎么安装?

在终端执行 npx skills add https://github.com/futuregerald/futuregerald-claude-plugin --skill javascri…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

javascript-testing-patterns 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

javascript-testing-patterns 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

javascript-testing-patterns 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

javascript-testing-patterns 用的人多吗?

公开数据是累计安装 291 次,近一周安装 12 次,下载 95 次,GitHub 4 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

javascript-testing-patterns 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
javascript-testing-patterns

JavaScript 测试模式 · 本页

2914需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

JavaScript Testing Patterns

Comprehensive testing patterns for modern JavaScript/TypeScript applications.

Core Principle: AAA Pattern

Every test follows Arrange, Act, Assert:

test('calculates total with discount', () => {
  // Arrange - Set up test data
  const cart = { items: [{ price: 100 }], discount: 0.1 }

  // Act - Execute the code under test
  const total = calculateTotal(cart)

  // Assert - Verify the result
  expect(total).toBe(90)
})

Framework Quick Reference

FrameworkRun TestsWatch ModeCoverage
Jestnpm testnpm test -- --watchnpm test -- --coverage
Vitestnpx vitestnpx vitest --watchnpx vitest --coverage
AdonisJS/Japanode ace testN/Anode ace test --coverage

Part 1: Jest/Vitest Patterns

Configuration

Jest (jest.config.js)

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  coverageThreshold: {
    global: { branches: 80, functions: 80, lines: 80, statements: 80 },
  },
  setupFilesAfterEnv: ['./jest.setup.ts'],
}

Vitest (vitest.config.ts)

import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: { provider: 'v8', reporter: ['text', 'json', 'html'] },
  },
})

Unit Testing Pure Functions

import { describe, it, expect } from 'vitest' // or jest

describe('calculateDiscount', () => {
  it('returns 0 for amounts below threshold', () => {
    expect(calculateDiscount(50)).toBe(0)
  })

  it('applies 10% discount for amounts over 100', () => {
    expect(calculateDiscount(200)).toBe(20)
  })

  it('handles edge case at threshold', () => {
    expect(calculateDiscount(100)).toBe(0)
    expect(calculateDiscount(100.01)).toBeCloseTo(10.001)
  })

  it('throws for negative amounts', () => {
    expect(() => calculateDiscount(-50)).toThrow('Amount cannot be negative')
  })
})

Testing Classes

describe('UserService', () => {
  let service: UserService

  beforeEach(() => {
    service = new UserService()
  })

  it('creates user with valid data', async () => {
    const user = await service.create({ email: 'test@example.com', name: 'Test' })

    expect(user.id).toBeDefined()
    expect(user.email).toBe('test@example.com')
  })

  it('throws for duplicate email', async () => {
    await service.create({ email: 'test@example.com', name: 'First' })

    await expect(service.create({ email: 'test@example.com', name: 'Second' })).rejects.toThrow(
      'Email already exists'
    )
  })
})

Mocking Strategies

Module Mocking

import { vi, describe, it, expect, beforeEach } from 'vitest'
import { sendEmail } from './email-service'
import { UserService } from './user-service'

vi.mock('./email-service', () => ({
  sendEmail: vi.fn().mockResolvedValue({ sent: true }),
}))

describe('UserService with mocked email', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('sends welcome email on registration', async () => {
    const service = new UserService()
    await service.register({ email: 'new@test.com' })

    expect(sendEmail).toHaveBeenCalledWith({
      to: 'new@test.com',
      template: 'welcome',
    })
  })
})

Dependency Injection (Preferred)

interface EmailSender {
  send(to: string, template: string): Promise<void>
}

class UserService {
  constructor(private emailSender: EmailSender) {}

  async register(data: { email: string }) {
    // ... create user
    await this.emailSender.send(data.email, 'welcome')
  }
}

// In tests - easy to mock
describe('UserService', () => {
  it('sends welcome email', async () => {
    const mockSender = { send: vi.fn().mockResolvedValue(undefined) }
    const service = new UserService(mockSender)

    await service.register({ email: 'test@example.com' })

    expect(mockSender.send).toHaveBeenCalledWith('test@example.com', 'welcome')
  })
})

Spying on Methods

it('logs errors to console', async () => {
  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})

  await service.handleError(new Error('test error'))

  expect(consoleSpy).toHaveBeenCalledWith('Error occurred:', expect.any(Error))
  consoleSpy.mockRestore()
})

Async Testing

describe('API client', () => {
  it('fetches data successfully', async () => {
    const data = await fetchUser(123)
    expect(data.id).toBe(123)
  })

  it('handles timeout', async () => {
    vi.useFakeTimers()

    const promise = fetchWithTimeout('/slow-endpoint', 1000)
    vi.advanceTimersByTime(1500)

    await expect(promise).rejects.toThrow('Request timeout')
    vi.useRealTimers()
  })

  it('retries on failure', async () => {
    const mockFetch = vi
      .fn()
      .mockRejectedValueOnce(new Error('Network error'))
      .mockRejectedValueOnce(new Error('Network error'))
      .mockResolvedValueOnce({ data: 'success' })

    const result = await fetchWithRetry(mockFetch, 3)

    expect(result.data).toBe('success')
    expect(mockFetch).toHaveBeenCalledTimes(3)
  })
})

Integration Testing with Supertest

import request from 'supertest'
import { app } from '../app'
import { db } from '../database'

describe('POST /api/users', () => {
  beforeAll(async () => {
    await db.migrate.latest()
  })

  afterEach(async () => {
    await db('users').truncate()
  })

  afterAll(async () => {
    await db.destroy()
  })

  it('creates user and returns 201', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'test@example.com', password: 'secure123' })
      .expect(201)

    expect(response.body).toMatchObject({
      id: expect.any(Number),
      email: 'test@example.com',
    })
  })

  it('returns 400 for invalid email', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'invalid', password: 'secure123' })
      .expect(400)

    expect(response.body.errors).toContainEqual(expect.objectContaining({ field: 'email' }))
  })
})

React Component Testing

import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'

describe('LoginForm', () => {
  it('submits with valid credentials', async () => {
    const onSubmit = vi.fn()
    render(<LoginForm onSubmit={onSubmit} />)

    await userEvent.type(screen.getByLabelText('Email'), 'test@example.com')
    await userEvent.type(screen.getByLabelText('Password'), 'password123')
    await userEvent.click(screen.getByRole('button', { name: /login/i }))

    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith({
        email: 'test@example.com',
        password: 'password123'
      })
    })
  })

  it('shows validation error for empty email', async () => {
    render(<LoginForm onSubmit={vi.fn()} />)

    await userEvent.click(screen.getByRole('button', { name: /login/i }))

    expect(screen.getByText('Email is required')).toBeInTheDocument()
  })

  it('disables submit button while loading', async () => {
    render(<LoginForm onSubmit={vi.fn()} isLoading />)

    expect(screen.getByRole('button', { name: /login/i })).toBeDisabled()
  })
})

Testing Custom Hooks

import { renderHook, act } from '@testing-library/react'
import { useCounter } from './useCounter'

describe('useCounter', () => {
  it('initializes with default value', () => {
    const { result } = renderHook(() => useCounter())
    expect(result.current.count).toBe(0)
  })

  it('increments count', () => {
    const { result } = renderHook(() => useCounter(5))

    act(() => {
      result.current.increment()
    })

    expect(result.current.count).toBe(6)
  })

  it('resets to initial value', () => {
    const { result } = renderHook(() => useCounter(10))

    act(() => {
      result.current.increment()
      result.current.increment()
      result.current.reset()
    })

    expect(result.current.count).toBe(10)
  })
})

Test Factories with Faker

import { faker } from '@faker-js/faker'

// factories/user.ts
export const createTestUser = (overrides = {}) => ({
  id: faker.string.uuid(),
  email: faker.internet.email(),
  name: faker.person.fullName(),
  createdAt: faker.date.past(),
  ...overrides
})

export const createTestUsers = (count: number, overrides = {}) =>
  Array.from({ length: count }, () => createTestUser(overrides))

// In tests
describe('UserList', () => {
  it('displays all users', () => {
    const users = createTestUsers(5)
    render(<UserList users={users} />)

    users.forEach(user => {
      expect(screen.getByText(user.name)).toBeInTheDocument()
    })
  })
})

Part 2: AdonisJS/Japa Patterns

Running Tests

# Run all tests
node ace test

# Run specific suite
node ace test functional
node ace test unit

# Run specific file
node ace test functional --files="user_auth"

# Run with coverage
node ace test --coverage

Test Structure

import { test } from '@japa/runner'

test.group('Feature | Description', (group) => {
  group.each.setup(() => {
    // runs before each test
  })

  group.each.teardown(() => {
    // runs after each test
  })

  test('specific behavior', async ({ assert }) => {
    const result = someFunction()
    assert.equal(result, expected)
  })
})

Database Testing with Transactions

import { test } from '@japa/runner'
import testUtils from '@adonisjs/core/services/test_utils'
import User from '#models/user'

test.group('Database tests', (group) => {
  // Wrap each test in a transaction that rolls back
  group.each.setup(() => testUtils.db().withGlobalTransaction())

  test('creates a record', async ({ assert }) => {
    const user = await User.create({ email: 'test@example.com' })
    assert.isNotNull(user.id)
    // Transaction rolls back - no cleanup needed
  })
})

HTTP Testing

Basic Request

test.group('API | Users', (group) => {
  group.each.setup(() => testUtils.db().withGlobalTransaction())

  test('GET /users returns list', async ({ client, assert }) => {
    const response = await client.get('/users')

    response.assertStatus(200)
    assert.isArray(response.body())
  })
})

Authenticated Requests

test('authenticated endpoint', async ({ client }) => {
  const user = await User.create({
    /* ... */
  })

  // Web session auth
  const response = await client.get('/dashboard').loginAs(user)

  // API token auth
  const apiResponse = await client.get('/api/v1/me').loginAs(user, 'api')

  response.assertStatus(200)
})

Testing Redirects

test('redirects after action', async ({ client }) => {
  const user = await User.create({
    /* ... */
  })

  const response = await client
    .post('/logout')
    .redirects(0) // Don't follow redirects
    .loginAs(user)

  response.assertStatus(302)
  response.assertHeader('location', '/login')
})

Form and JSON Submissions

// Form data
const response = await client
  .post('/posts')
  .form({ title: 'My Post', description: 'A test post' })
  .loginAs(user)

// JSON API
const response = await client
  .post('/api/v1/posts')
  .json({ title: 'My Post', description: 'A test post' })
  .loginAs(user, 'api')

// AJAX request
const response = await client
  .post('/comments')
  .header('X-Requested-With', 'XMLHttpRequest')
  .form({ content: 'Test comment' })
  .loginAs(user)

Japa Assertions

test('assertions example', async ({ assert }) => {
  // Equality
  assert.equal(actual, expected)
  assert.deepEqual(obj1, obj2)

  // Truthiness
  assert.isTrue(value)
  assert.isFalse(value)
  assert.isNull(value)
  assert.isNotNull(value)

  // Types
  assert.isString(value)
  assert.isArray(value)
  assert.isObject(value)

  // Arrays/Objects
  assert.lengthOf(array, 3)
  assert.include(array, item)
  assert.property(obj, 'key')
  assert.containsSubset(obj, { key: 'value' })

  // Exceptions
  assert.throws(() => throwingFn(), Error)
  await assert.rejects(async () => asyncThrowingFn(), Error)
})

Response Assertions

response.assertStatus(200)
response.assertHeader('content-type', 'application/json')
response.assertHeader('location', '/dashboard')
response.assertBody({ success: true })
response.assertBodyContains({ id: 1 })
response.assertTextIncludes('Welcome')

Testing with Sinon Mocks

import sinon from 'sinon'
import EmailService from '#services/email_service'

test.group('With mocks', (group) => {
  group.each.teardown(() => {
    sinon.restore()
  })

  test('sends email on registration', async ({ assert }) => {
    const sendStub = sinon.stub(EmailService, 'send').resolves()

    await UserService.register({ email: 'test@example.com' })

    assert.isTrue(sendStub.calledOnce)
    assert.equal(sendStub.firstCall.args[0], 'test@example.com')
  })
})

Common Test Patterns

Auth Required Routes

test('requires authentication', async ({ client }) => {
  const response = await client.get('/dashboard').redirects(0)
  response.assertStatus(302)
  response.assertHeader('location', '/login')
})

test('API returns 401 without auth', async ({ client }) => {
  const response = await client.get('/api/v1/me')
  response.assertStatus(401)
})

Validation Errors

test('validates required fields', async ({ client }) => {
  const user = await User.create({
    /* ... */
  })

  const response = await client.post('/api/v1/posts').json({}).loginAs(user, 'api')

  response.assertStatus(422)
  response.assertBodyContains({ code: 'E_VALIDATION' })
})

Authorization

test('denies access to other user resources', async ({ client }) => {
  const owner = await User.create({ email: 'owner@test.com' })
  const other = await User.create({ email: 'other@test.com' })
  const resource = await Resource.create({ ownerId: owner.id })

  const response = await client
    .patch(`/api/v1/resources/${resource.id}`)
    .json({ title: 'Hacked' })
    .loginAs(other, 'api')

  response.assertStatus(403)
})

Anti-Patterns to Avoid

Don't Test Implementation Details

// BAD
test('calls internal method', async () => {
  const spy = vi.spyOn(service, '_internalHelper')
  await service.doThing()
  expect(spy).toHaveBeenCalled()
})

// GOOD - Test observable behavior
test('produces correct output', async () => {
  const result = await service.doThing()
  expect(result).toEqual(expected)
})

Don't Over-Mock

// BAD - Testing mock, not real code
test('calls database', async () => {
  const mockDb = { query: vi.fn().mockResolvedValue([]) }
  const service = new UserService(mockDb)
  await service.getUsers()
  expect(mockDb.query).toHaveBeenCalled()
})

// GOOD - Test real behavior with test database
test('returns users from database', async () => {
  await User.create({ name: 'Test' })
  const users = await service.getUsers()
  expect(users).toHaveLength(1)
})

Don't Forget Cleanup

// BAD - Pollutes database
test.group('Tests', () => {
  test('creates record', async () => {
    await User.create({
      /* ... */
    }) // Persists!
  })
})

// GOOD - Uses transaction rollback
test.group('Tests', (group) => {
  group.each.setup(() => testUtils.db().withGlobalTransaction())

  test('creates record', async () => {
    await User.create({
      /* ... */
    }) // Rolls back
  })
})

File Organization

tests/
├── functional/           # HTTP/integration tests
│   ├── auth.spec.ts
│   ├── users.spec.ts
│   └── api/
│       └── users.spec.ts
├── unit/                 # Unit tests
│   └── services/
│       └── user_service.spec.ts
├── factories/            # Test data factories
│   └── user.ts
└── bootstrap.ts          # Test setup

Quick Reference

ActionJest/VitestAdonisJS/Japa
Run testsnpm testnode ace test
Run filenpm test -- path/to/filenode ace test --files="name"
Coverage--coverage--coverage
Mock functionvi.fn() / jest.fn()sinon.stub()
Spyvi.spyOn()sinon.spy()
Auth requestN/A (manual).loginAs(user)
Don't redirectN/A.redirects(0)
Form data.send().form()
JSON data.send().json()
Assert statusexpect(res.status).toBe(200)response.assertStatus(200)

平台分布

Codex

39.45%
按下载量换算37

Claude

30.23%
按下载量换算29

Cursor

17.18%
按下载量换算16

Gemini CLI

9.64%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills