Skip to Content
guidesTesting

Last Updated: 3/11/2026


Testing

Testing Hono applications is straightforward. Create requests, pass them to your app, and validate responses.

Basic Testing

Use app.request() to test your routes:

import { describe, test, expect } from 'vitest' import { Hono } from 'hono' const app = new Hono() app.get('/posts', (c) => { return c.text('Many posts') }) describe('Example', () => { test('GET /posts', async () => { const res = await app.request('/posts') expect(res.status).toBe(200) expect(await res.text()).toBe('Many posts') }) })

Testing POST Requests

Simple POST

app.post('/posts', (c) => { return c.json({ message: 'Created' }, 201, { 'X-Custom': 'Thank you', }) }) test('POST /posts', async () => { const res = await app.request('/posts', { method: 'POST', }) expect(res.status).toBe(201) expect(res.headers.get('X-Custom')).toBe('Thank you') expect(await res.json()).toEqual({ message: 'Created' }) })

POST with JSON

test('POST /posts with JSON', async () => { const res = await app.request('/posts', { method: 'POST', body: JSON.stringify({ message: 'hello hono' }), headers: new Headers({ 'Content-Type': 'application/json' }), }) expect(res.status).toBe(201) expect(await res.json()).toEqual({ message: 'Created' }) })

POST with Form Data

test('POST /posts with form data', async () => { const formData = new FormData() formData.append('message', 'hello') const res = await app.request('/posts', { method: 'POST', body: formData, }) expect(res.status).toBe(201) })

Using Request Objects

Pass a Request instance directly:

test('POST /posts with Request', async () => { const req = new Request('http://localhost/posts', { method: 'POST', }) const res = await app.request(req) expect(res.status).toBe(201) })

Testing with Environment Variables

Pass environment variables as the third parameter:

const MOCK_ENV = { API_HOST: 'example.com', DB: { prepare: () => { /* mocked D1 */ }, }, } test('GET /posts', async () => { const res = await app.request('/posts', {}, MOCK_ENV) expect(res.status).toBe(200) })

Testing Middleware

Test middleware behavior:

import { basicAuth } from 'hono/basic-auth' const app = new Hono() app.use( '/admin/*', basicAuth({ username: 'admin', password: 'secret', }) ) app.get('/admin', (c) => c.text('Admin panel')) test('Unauthorized access', async () => { const res = await app.request('/admin') expect(res.status).toBe(401) }) test('Authorized access', async () => { const res = await app.request('/admin', { headers: { Authorization: 'Basic ' + btoa('admin:secret'), }, }) expect(res.status).toBe(200) expect(await res.text()).toBe('Admin panel') })

Runtime-Specific Testing

Cloudflare Workers

Use @cloudflare/vitest-pool-workers:

npm install -D @cloudflare/vitest-pool-workers

See Cloudflare’s Vitest integration guide .

Deno

Use Deno’s built-in test runner:

import { assertEquals } from '@std/assert' import { Hono } from 'hono' Deno.test('GET /posts', async () => { const app = new Hono() app.get('/posts', (c) => c.text('Many posts')) const res = await app.request('http://localhost/posts') assertEquals(res.status, 200) assertEquals(await res.text(), 'Many posts') })

Bun

Use bun:test:

import { describe, expect, it } from 'bun:test' import { Hono } from 'hono' const app = new Hono() app.get('/posts', (c) => c.text('Many posts')) describe('API tests', () => { it('Should return posts', async () => { const res = await app.request('/posts') expect(res.status).toBe(200) expect(await res.text()).toBe('Many posts') }) })

Testing Helper

For type-safe testing, use the testing helper:

import { testClient } from 'hono/testing' const client = testClient(app) const res = await client.posts.$get() expect(res.status).toBe(200)

Next Steps

Explore Helpers and Built-in Middleware for more features.