Testing
Test your application with the built-in TestApp utility.
MiiaJS provides a TestApp class for writing unit and integration tests. Import it from @miiajs/core/testing.
TestApp API
import { TestApp } from '@miiajs/core/testing'
const app = await TestApp.create(AppModule)
.override(UserService, mockUserService)
.compile()
Builder methods
| Method | Description |
|---|---|
TestApp.create(...modules) | Create a test app from modules |
.provide(...providers) | Add additional providers |
.override(token, value) | Replace a provider with a mock |
.use(...middlewares) | Add middleware |
.useGuard(...guards) | Add guards |
.compile() | Build the test app (returns Promise<TestApp>) |
Instance methods
| Method | Description |
|---|---|
.request(method, path, options?) | Send an HTTP request |
.resolve(token) | Resolve a provider from the container |
.close() | Clean up resources |
Integration tests
Test your HTTP endpoints end-to-end:
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { TestApp } from '@miiajs/core/testing'
import { AppModule } from '../src/app.module.js'
describe('UserController', () => {
let app: TestApp
beforeAll(async () => {
app = await TestApp.create(AppModule)
.override(UserService, {
findAll: () => [{ id: 1, name: 'Alice' }],
create: (data) => ({ id: 2, ...data }),
})
.compile()
})
afterAll(() => app.close())
it('GET /users should return users', async () => {
const res = await app.request('GET', '/users')
expect(res.status).toBe(200)
expect(await res.json()).toEqual([{ id: 1, name: 'Alice' }])
})
it('POST /users should create a user', async () => {
const res = await app.request('POST', '/users', {
body: { name: 'Bob', email: 'bob@test.com' },
})
expect(res.status).toBe(201)
expect(await res.json()).toMatchObject({ name: 'Bob' })
})
it('POST /users should validate body', async () => {
const res = await app.request('POST', '/users', {
body: { invalid: true },
})
expect(res.status).toBe(422)
})
})
Unit tests
Test individual services with mocked dependencies:
describe('UserService', () => {
let app: TestApp
let service: UserService
beforeAll(async () => {
app = await TestApp.create()
.provide(UserService)
.override(DatabaseService, {
query: () => [{ id: 1, name: 'Alice' }],
})
.compile()
service = app.resolve(UserService)
})
afterAll(() => app.close())
it('should find all users', async () => {
const result = await service.findAll()
expect(result).toEqual([{ id: 1, name: 'Alice' }])
})
})
Request options
The request() method accepts an options object:
const res = await app.request('POST', '/api/users', {
body: { name: 'Alice' },
headers: {
'Authorization': 'Bearer my-token',
'X-Custom': 'value',
},
query: {
page: '1',
limit: '10',
},
})
| Option | Type | Description |
|---|---|---|
body | any | Request body (auto-serialized to JSON) |
headers | Record<string, string> | Request headers |
query | Record<string, string> | Query parameters |
Testing with auth
import { JwtService } from '@miiajs/auth/jwt'
describe('Protected routes', () => {
let app: TestApp
let token: string
beforeAll(async () => {
app = await TestApp.create(AppModule)
.override(UserService, mockUserService)
.compile()
const jwt = app.resolve(JwtService)
token = await jwt.sign({ sub: '1', role: 'admin' })
})
it('should reject unauthenticated requests', async () => {
const res = await app.request('GET', '/api/users/1')
expect(res.status).toBe(401)
})
it('should allow authenticated requests', async () => {
const res = await app.request('GET', '/api/users/1', {
headers: { Authorization: `Bearer ${token}` },
})
expect(res.status).toBe(200)
})
})