Token导航 LogoToken导航

Agent Skill · 研究检索

hono-testing(荣誉测试)

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

hono-testing 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为接触敏感数据;许可证为 MIT,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 21,395。

总安装 4,872周安装 201GitHub Stars 39下载 1,592核验 2026-05-01

安装说明

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

安装方式

通过对话安装

复制提示词发给支持本地命令或 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

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

用于辅助测试设计和回归验证。hono-testing 属于研究检索类 Skill,可作为该场景下的辅助能力补充。支持单元测试、端到端测试和用例整理。需要确认项目测试框架和运行命令。涉及浏览器或服务时应区分环境。

适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。避免为了通过测试而改坏真实逻辑。

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

上手步骤

4 步
  1. 1

    确认自己的宿主工具

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

  2. 2

    拿到安装命令

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

  3. 3

    安装后重启宿主

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

  4. 4

    按最小权限试用

    它需要接触敏感数据,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

hono-testing 怎么安装?

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

hono-testing 支持哪些 AI 工具?

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

hono-testing 需要什么权限?

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

hono-testing 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

hono-testing 用的人多吗?

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

hono-testing 安全吗?

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

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

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 需要接触敏感数据,装到常用环境前要先评估影响面。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 环境里有大量密钥、凭证,不方便给第三方 Skill 读取的人。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

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

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
hono-testing

荣誉测试 · 本页

4,87239接触敏感数据MIT通过

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

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

平台分布

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