Token导航 LogoToken导航TokenDH.com
研究检索敏感数据github未标认证来源可访问clear审计通过

hono-testing荣誉测试

Agent Skill

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

总安装

4,872

周安装

201

GitHub Stars

39

下载量

1,592
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

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

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

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill hono-testing

简介

用于辅助测试设计和回归验证。hono-testing 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

  • 支持单元测试、端到端测试和用例整理。
  • 需要确认项目测试框架和运行命令。
  • 涉及浏览器或服务时应区分环境。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。
  • 避免为了通过测试而改坏真实逻辑。

SKILL.md

Hono Testing Patterns

Overview

Hono provides a simple testing approach: create a Request, pass it to your app, and validate the Response. The framework includes a typed test client for even better DX.

Key Features:

  • Simple app.request() API
  • Typed test client with full inference
  • Environment mocking for Workers
  • Works with Vitest, Jest, or any test runner

When to Use This Skill

Use Hono testing when:

  • Writing unit tests for route handlers
  • Integration testing API endpoints
  • Testing middleware behavior
  • Mocking Cloudflare Workers bindings
  • Validating request/response cycles

Basic Testing

Using app.request()

import { Hono } from 'hono'
import { describe, it, expect } from 'vitest'

const app = new Hono()

app.get('/hello', (c) => c.text('Hello!'))
app.get('/json', (c) => c.json({ message: 'Hello' }))

describe('Basic routes', () => {
  it('should return text', async () => {
    const res = await app.request('/hello')

    expect(res.status).toBe(200)
    expect(await res.text()).toBe('Hello!')
  })

  it('should return JSON', async () => {
    const res = await app.request('/json')

    expect(res.status).toBe(200)
    expect(res.headers.get('Content-Type')).toContain('application/json')
    expect(await res.json()).toEqual({ message: 'Hello' })
  })
})

Request Options

// GET with query params
const res = await app.request('/search?q=hono&page=1')

// POST with JSON body
const res = await app.request('/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
})

// POST with form data
const formData = new FormData()
formData.append('name', 'Alice')
formData.append('email', 'alice@example.com')

const res = await app.request('/users', {
  method: 'POST',
  body: formData
})

// With custom headers
const res = await app.request('/protected', {
  headers: {
    'Authorization': 'Bearer token123',
    'X-Custom-Header': 'value'
  }
})

// DELETE request
const res = await app.request('/users/123', {
  method: 'DELETE'
})

Typed Test Client

The test client provides full type inference:

import { Hono } from 'hono'
import { testClient } from 'hono/testing'
import { describe, it, expect } from 'vitest'

const app = new Hono()
  .get('/users', (c) => c.json({ users: [] }))
  .post('/users', async (c) => {
    const body = await c.req.json()
    return c.json({ id: '1', ...body }, 201)
  })
  .get('/users/:id', (c) => {
    return c.json({ id: c.req.param('id'), name: 'Alice' })
  })

describe('Users API', () => {
  const client = testClient(app)

  it('should list users', async () => {
    const res = await client.users.$get()

    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.users).toEqual([])
  })

  it('should create user', async () => {
    const res = await client.users.$post({
      json: { name: 'Alice', email: 'alice@example.com' }
    })

    expect(res.status).toBe(201)
    const data = await res.json()
    expect(data.name).toBe('Alice')
  })

  it('should get user by id', async () => {
    const res = await client.users[':id'].$get({
      param: { id: '123' }
    })

    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.id).toBe('123')
  })
})

Testing with Validation

import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

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

app.post('/users', zValidator('json', createUserSchema), async (c) => {
  const data = c.req.valid('json')
  return c.json({ id: '1', ...data }, 201)
})

describe('Validation', () => {
  it('should accept valid data', async () => {
    const res = await app.request('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        name: 'Alice',
        email: 'alice@example.com'
      })
    })

    expect(res.status).toBe(201)
  })

  it('should reject invalid email', async () => {
    const res = await app.request('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        name: 'Alice',
        email: 'invalid-email'
      })
    })

    expect(res.status).toBe(400)
  })

  it('should reject missing name', async () => {
    const res = await app.request('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email: 'alice@example.com'
      })
    })

    expect(res.status).toBe(400)
  })
})

Mocking Environment (Cloudflare Workers)

Mock Bindings

import { Hono } from 'hono'

type Bindings = {
  DB: D1Database
  KV: KVNamespace
  API_KEY: string
}

const app = new Hono<{ Bindings: Bindings }>()

app.get('/data', async (c) => {
  const result = await c.env.DB.prepare('SELECT * FROM users').all()
  return c.json(result)
})

app.get('/config', (c) => {
  return c.json({ apiKey: c.env.API_KEY.slice(0, 4) + '...' })
})

describe('With mocked bindings', () => {
  // Mock D1 database
  const mockDB = {
    prepare: () => ({
      all: async () => ({ results: [{ id: 1, name: 'Alice' }] }),
      first: async () => ({ id: 1, name: 'Alice' }),
      run: async () => ({ success: true })
    })
  }

  // Mock KV namespace
  const mockKV = {
    get: async (key: string) => 'cached-value',
    put: async (key: string, value: string) => {},
    delete: async (key: string) => {}
  }

  const mockEnv: Bindings = {
    DB: mockDB as unknown as D1Database,
    KV: mockKV as unknown as KVNamespace,
    API_KEY: 'test-api-key-12345'  // pragma: allowlist secret
  }

  it('should use mocked database', async () => {
    const res = await app.request('/data', {}, mockEnv)

    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.results).toHaveLength(1)
  })

  it('should use mocked API key', async () => {
    const res = await app.request('/config', {}, mockEnv)

    const data = await res.json()
    expect(data.apiKey).toBe('test...')
  })
})

Using Miniflare

For more realistic Cloudflare Workers testing:

import { Miniflare } from 'miniflare'
import { describe, it, expect, beforeAll, afterAll } from 'vitest'

describe('With Miniflare', () => {
  let mf: Miniflare

  beforeAll(async () => {
    mf = new Miniflare({
      script: `
        import app from './src/index'
        export default app
      `,
      modules: true,
      d1Databases: ['DB'],
      kvNamespaces: ['KV']
    })
  })

  afterAll(async () => {
    await mf.dispose()
  })

  it('should work with real bindings', async () => {
    const res = await mf.dispatchFetch('http://localhost/data')
    expect(res.status).toBe(200)
  })
})

Testing Middleware

import { Hono } from 'hono'
import { createMiddleware } from 'hono/factory'

// Middleware to test
const authMiddleware = createMiddleware(async (c, next) => {
  const token = c.req.header('Authorization')?.replace('Bearer ', '')

  if (!token) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  if (token !== 'valid-token') {
    return c.json({ error: 'Invalid token' }, 403)
  }

  c.set('userId', 'user-123')
  await next()
})

const app = new Hono()

app.use('/protected/*', authMiddleware)

app.get('/protected/data', (c) => {
  const userId = c.get('userId')
  return c.json({ userId, data: 'secret' })
})

describe('Auth middleware', () => {
  it('should reject request without token', async () => {
    const res = await app.request('/protected/data')

    expect(res.status).toBe(401)
    expect(await res.json()).toEqual({ error: 'Unauthorized' })
  })

  it('should reject invalid token', async () => {
    const res = await app.request('/protected/data', {
      headers: { 'Authorization': 'Bearer invalid' }
    })

    expect(res.status).toBe(403)
    expect(await res.json()).toEqual({ error: 'Invalid token' })
  })

  it('should allow valid token', async () => {
    const res = await app.request('/protected/data', {
      headers: { 'Authorization': 'Bearer valid-token' }
    })

    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.userId).toBe('user-123')
  })
})

Testing Error Handling

import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'

const app = new Hono()

app.get('/error', () => {
  throw new HTTPException(500, { message: 'Something went wrong' })
})

app.get('/not-found', (c) => {
  return c.notFound()
})

app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return c.json({ error: err.message }, err.status)
  }
  return c.json({ error: 'Internal error' }, 500)
})

app.notFound((c) => {
  return c.json({ error: 'Not found' }, 404)
})

describe('Error handling', () => {
  it('should handle HTTPException', async () => {
    const res = await app.request('/error')

    expect(res.status).toBe(500)
    expect(await res.json()).toEqual({ error: 'Something went wrong' })
  })

  it('should handle not found', async () => {
    const res = await app.request('/unknown-route')

    expect(res.status).toBe(404)
    expect(await res.json()).toEqual({ error: 'Not found' })
  })
})

Testing File Uploads

import { Hono } from 'hono'

const app = new Hono()

app.post('/upload', async (c) => {
  const formData = await c.req.formData()
  const file = formData.get('file') as File

  if (!file) {
    return c.json({ error: 'No file provided' }, 400)
  }

  return c.json({
    filename: file.name,
    size: file.size,
    type: file.type
  })
})

describe('File upload', () => {
  it('should handle file upload', async () => {
    const file = new File(['hello world'], 'test.txt', { type: 'text/plain' })
    const formData = new FormData()
    formData.append('file', file)

    const res = await app.request('/upload', {
      method: 'POST',
      body: formData
    })

    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.filename).toBe('test.txt')
    expect(data.size).toBe(11)
    expect(data.type).toBe('text/plain')
  })

  it('should reject missing file', async () => {
    const formData = new FormData()

    const res = await app.request('/upload', {
      method: 'POST',
      body: formData
    })

    expect(res.status).toBe(400)
  })
})

Test Setup Patterns

Vitest Configuration

// vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      reporter: ['text', 'json', 'html'],
      exclude: ['node_modules/', 'dist/']
    }
  }
})

Test Utilities

// test/utils.ts
import { Hono } from 'hono'
import type { Bindings } from '../src/types'

export function createTestApp() {
  // Return fresh app instance for each test
  return new Hono<{ Bindings: Bindings }>()
}

export function createMockEnv(overrides: Partial<Bindings> = {}): Bindings {
  return {
    DB: createMockDB(),
    KV: createMockKV(),
    API_KEY: 'test-key',  // pragma: allowlist secret
    ...overrides
  }
}

export function createMockDB() {
  return {
    prepare: (sql: string) => ({
      bind: (...args: any[]) => ({
        all: async () => ({ results: [] }),
        first: async () => null,
        run: async () => ({ success: true })
      }),
      all: async () => ({ results: [] }),
      first: async () => null,
      run: async () => ({ success: true })
    })
  }
}

export function createMockKV() {
  const store = new Map<string, string>()

  return {
    get: async (key: string) => store.get(key) ?? null,
    put: async (key: string, value: string) => { store.set(key, value) },
    delete: async (key: string) => { store.delete(key) }
  }
}

Using Test Utilities

import { describe, it, expect, beforeEach } from 'vitest'
import { createTestApp, createMockEnv } from './utils'
import { setupRoutes } from '../src/routes'

describe('API Tests', () => {
  let app: ReturnType<typeof createTestApp>
  let env: ReturnType<typeof createMockEnv>

  beforeEach(() => {
    app = createTestApp()
    env = createMockEnv()
    setupRoutes(app)
  })

  it('should work with fresh instances', async () => {
    const res = await app.request('/api/health', {}, env)
    expect(res.status).toBe(200)
  })
})

Quick Reference

app.request() Signature

app.request(
  path: string,
  options?: RequestInit,
  env?: Bindings
): Promise<Response>

Common Assertions

// Status
expect(res.status).toBe(200)
expect(res.ok).toBe(true)

// Headers
expect(res.headers.get('Content-Type')).toContain('application/json')
expect(res.headers.get('X-Custom')).toBe('value')

// Body
expect(await res.text()).toBe('Hello')
expect(await res.json()).toEqual({ key: 'value' })

// Response properties
expect(res.redirected).toBe(false)
expect(res.url).toBe('http://localhost/path')

Test Client Methods

const client = testClient(app)

client.path.$get()
client.path.$post({ json: {} })
client.path[':id'].$get({ param: { id: '1' } })
client.path.$get({ query: { page: 1 } })
client.path.$post({ header: { 'X-Custom': 'v' } })

Related Skills

  • hono-core - Framework fundamentals
  • hono-middleware - Middleware patterns
  • hono-validation - Request validation

Version: Hono 4.x Last Updated: January 2025 License: MIT

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

32.45%
按下载量换算517

OpenCode

24.63%
按下载量换算392

Gemini CLI

16.68%
按下载量换算266

Cursor

11.5%
按下载量换算183

github-copilot

8.39%
按下载量换算134

Antigravity

3.74%
按下载量换算60

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills