> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/honojs/hono/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Test your Hono application with the testing helper

The Testing helper provides utilities for testing your Hono applications with type-safe request methods.

## Import

```typescript theme={null}
import { testClient } from 'hono/testing'
```

## Basic Usage

Create a type-safe client for testing your app:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .get('/posts', (c) => c.json({ posts: [] }))
  .post('/posts', (c) => c.json({ id: 1 }, 201))

const client = testClient(app)

// Make requests
const res = await client.posts.$get()
expect(res.status).toBe(200)

const data = await res.json()
expect(data).toEqual({ posts: [] })
```

## Features

### Type-Safe Requests

The test client provides full TypeScript support:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

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

const client = testClient(app)

// Type-safe requests
const getRes = await client.users[':id'].$get({
  param: { id: '123' }
})

const postRes = await client.users.$post({
  json: { name: 'Jane' }
})
```

### Query Parameters

Test routes with query parameters:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .get('/search', (c) => {
    const query = c.req.query('q')
    return c.json({ query })
  })

const client = testClient(app)

const res = await client.search.$get({
  query: { q: 'hono' }
})

const data = await res.json()
expect(data.query).toBe('hono')
```

### Request Headers

Include custom headers in requests:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .get('/protected', (c) => {
    const auth = c.req.header('Authorization')
    if (!auth) return c.text('Unauthorized', 401)
    return c.json({ data: 'secret' })
  })

const client = testClient(app)

const res = await client.protected.$get({
  headers: {
    Authorization: 'Bearer token123'
  }
})

expect(res.status).toBe(200)
```

### JSON Request Body

Send JSON data in requests:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .post('/api/data', async (c) => {
    const body = await c.req.json<{ name: string; age: number }>()
    return c.json({ received: body })
  })

const client = testClient(app)

const res = await client.api.data.$post({
  json: { name: 'Alice', age: 30 }
})

const data = await res.json()
expect(data.received.name).toBe('Alice')
```

### Form Data

Test file uploads and form submissions:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .post('/upload', async (c) => {
    const body = await c.req.parseBody()
    return c.json({ filename: body.file?.name })
  })

const client = testClient(app)

const formData = new FormData()
formData.append('file', new Blob(['content']), 'test.txt')

const res = await client.upload.$post({
  body: formData
})
```

## Environment Variables

Test with environment bindings:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

type Bindings = {
  API_KEY: string
  DATABASE_URL: string
}

const app = new Hono<{ Bindings: Bindings }>()
  .get('/config', (c) => {
    return c.json({
      apiKey: c.env.API_KEY,
      dbUrl: c.env.DATABASE_URL
    })
  })

const client = testClient(app, {
  API_KEY: 'test-key',
  DATABASE_URL: 'postgres://localhost'
})

const res = await client.config.$get()
const data = await res.json()

expect(data.apiKey).toBe('test-key')
```

## Execution Context

Provide execution context for testing:

```typescript theme={null}
import { testClient } from 'hono/testing'
import type { ExecutionContext } from 'hono'

const mockContext: ExecutionContext = {
  waitUntil: (promise: Promise<any>) => {},
  passThroughOnException: () => {},
}

const client = testClient(
  app,
  undefined, // Bindings
  mockContext
)
```

## Client Options

Customize the test client:

```typescript theme={null}
import { testClient } from 'hono/testing'

const client = testClient(app, undefined, undefined, {
  headers: {
    'User-Agent': 'test-client',
    'Accept-Language': 'en-US'
  }
})

// All requests will include these headers
const res = await client.api.$get()
```

## Testing Patterns

### Unit Tests

Test individual routes:

```typescript theme={null}
import { describe, it, expect } from 'vitest'
import { testClient } from 'hono/testing'

describe('API Routes', () => {
  it('should return user list', async () => {
    const client = testClient(app)
    const res = await client.users.$get()
    
    expect(res.status).toBe(200)
    const data = await res.json()
    expect(Array.isArray(data.users)).toBe(true)
  })
  
  it('should create a user', async () => {
    const client = testClient(app)
    const res = await client.users.$post({
      json: { name: 'Test User' }
    })
    
    expect(res.status).toBe(201)
    const data = await res.json()
    expect(data.name).toBe('Test User')
  })
})
```

### Integration Tests

Test complete workflows:

```typescript theme={null}
import { describe, it, expect } from 'vitest'
import { testClient } from 'hono/testing'

describe('User workflow', () => {
  it('should create and retrieve user', async () => {
    const client = testClient(app)
    
    // Create user
    const createRes = await client.users.$post({
      json: { name: 'Alice', email: 'alice@example.com' }
    })
    
    expect(createRes.status).toBe(201)
    const created = await createRes.json()
    
    // Retrieve user
    const getRes = await client.users[':id'].$get({
      param: { id: created.id }
    })
    
    expect(getRes.status).toBe(200)
    const retrieved = await getRes.json()
    expect(retrieved.name).toBe('Alice')
  })
})
```

### Testing Middleware

Test middleware behavior:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'

const app = new Hono()
  .use('*', logger())
  .use('*', cors())
  .get('/api', (c) => c.json({ ok: true }))

const client = testClient(app)

const res = await client.api.$get()
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
```

### Testing Error Cases

Test error handling:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .get('/error', (c) => {
    throw new Error('Something went wrong')
  })
  .onError((err, c) => {
    return c.json({ error: err.message }, 500)
  })

const client = testClient(app)

const res = await client.error.$get()
expect(res.status).toBe(500)

const data = await res.json()
expect(data.error).toBe('Something went wrong')
```

## Testing with Different Test Frameworks

### Vitest

```typescript theme={null}
import { describe, it, expect, beforeEach } from 'vitest'
import { testClient } from 'hono/testing'
import { app } from './app'

describe('API', () => {
  let client: ReturnType<typeof testClient>
  
  beforeEach(() => {
    client = testClient(app)
  })
  
  it('works', async () => {
    const res = await client.index.$get()
    expect(res.status).toBe(200)
  })
})
```

### Jest

```typescript theme={null}
import { testClient } from 'hono/testing'
import { app } from './app'

describe('API', () => {
  let client: ReturnType<typeof testClient>
  
  beforeEach(() => {
    client = testClient(app)
  })
  
  test('returns 200', async () => {
    const res = await client.index.$get()
    expect(res.status).toBe(200)
  })
})
```

### Node Test Runner

```typescript theme={null}
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { testClient } from 'hono/testing'
import { app } from './app'

describe('API', () => {
  it('returns 200', async () => {
    const client = testClient(app)
    const res = await client.index.$get()
    assert.strictEqual(res.status, 200)
  })
})
```

## URL Generation

Generate URLs for routes:

```typescript theme={null}
import { testClient } from 'hono/testing'

const client = testClient(app)

const url = client.users[':id'].$url({
  param: { id: '123' },
  query: { include: 'posts' }
})

console.log(url.pathname) // /users/123
console.log(url.searchParams.get('include')) // posts
```

## Mock Responses

Mock external dependencies:

```typescript theme={null}
import { Hono } from 'hono'
import { testClient } from 'hono/testing'

const app = new Hono()
  .get('/external', async (c) => {
    const res = await fetch('https://api.example.com/data')
    const data = await res.json()
    return c.json(data)
  })

// Mock fetch
globalThis.fetch = async () => {
  return new Response(JSON.stringify({ mocked: true }))
}

const client = testClient(app)
const res = await client.external.$get()
const data = await res.json()

expect(data.mocked).toBe(true)
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Type Safety" icon="shield">
    Leverage TypeScript for compile-time type checking of requests
  </Card>

  <Card title="Isolate Tests" icon="flask">
    Create a new test client for each test to avoid side effects
  </Card>

  <Card title="Test Real Paths" icon="route">
    Test actual user paths through your application
  </Card>

  <Card title="Mock External APIs" icon="plug">
    Mock external dependencies for faster, reliable tests
  </Card>
</CardGroup>

<Note>
  The test client uses the same Hono client (`hc`) used in production, ensuring your tests reflect real usage.
</Note>

<Info>
  Test clients run against your Hono app directly without HTTP overhead, making tests fast and reliable.
</Info>
