Skip to Content
guidesTesting Applications

Last Updated: 3/9/2026


Testing Applications

Testing is essential for maintaining reliable Hono applications. Hono makes testing straightforward—you create a Request, pass it to your app, and validate the Response. The same testing approach works across all runtimes.

Testing with app.request()

The app.request() method is the primary tool for testing Hono applications. It simulates HTTP requests without needing a running server.

Basic GET Request Test

Consider an application with this endpoint:

app.get('/posts', (c) => { return c.text('Many posts') })

Test it like this:

import { describe, test, expect } from 'vitest' describe('GET /posts', () => { test('returns 200 with text response', async () => { const res = await app.request('/posts') expect(res.status).toBe(200) expect(await res.text()).toBe('Many posts') }) })

Testing POST Requests

For a POST endpoint:

app.post('/posts', (c) => { return c.json( { message: 'Created' }, 201, { 'X-Custom': 'Thank you' } ) })

Test the status, headers, and JSON response:

test('POST /posts returns 201', 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', }) })

Testing with Request Bodies

JSON Request Body

When sending JSON data, include the Content-Type header:

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

Note: The Content-Type header is required when testing JSON or form validation. Without it, the request body will not be parsed correctly.

Form Data Request Body

For multipart form data:

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

Using Request Objects

You can also pass a complete Request object:

test('POST /posts with Request object', async () => { const req = new Request('http://localhost/posts', { method: 'POST', body: JSON.stringify({ title: 'Test' }), headers: { 'Content-Type': 'application/json' }, }) const res = await app.request(req) expect(res.status).toBe(201) })

Mocking Environment Variables

To test code that uses c.env, pass a mock environment object as the third parameter:

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

This is particularly useful for testing Cloudflare Workers bindings like KV, D1, or R2.

Testing Middleware

Test middleware by verifying its effects on the response:

import { basicAuth } from 'hono/basic-auth' app.use( '/admin/*', basicAuth({ username: 'admin', password: 'secret', }) ) app.get('/admin/dashboard', (c) => c.text('Admin Dashboard')) test('requires authentication', async () => { // Without credentials const res1 = await app.request('/admin/dashboard') expect(res1.status).toBe(401) // With credentials const credentials = btoa('admin:secret') const res2 = await app.request('/admin/dashboard', { headers: { Authorization: `Basic ${credentials}` }, }) expect(res2.status).toBe(200) })

Testing Error Handling

Test custom error handlers:

app.onError((err, c) => { return c.json({ error: err.message }, 500) }) app.get('/error', () => { throw new Error('Something went wrong') }) test('handles errors correctly', async () => { const res = await app.request('/error') expect(res.status).toBe(500) expect(await res.json()).toEqual({ error: 'Something went wrong', }) })

Testing with Vitest

For Cloudflare Workers, use @cloudflare/vitest-pool-workers  for the most accurate testing environment.

Install it:

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

Configure vitest.config.ts:

import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' export default defineWorkersConfig({ test: { poolOptions: { workers: { wrangler: { configPath: './wrangler.toml' }, }, }, }, })

See the Cloudflare Vitest integration docs  for complete setup instructions.

Type-Safe Testing with Testing Helper

For advanced type-safe testing, use the testing helper which provides typed test clients.

What’s Next