Mengimplementasikan testing di ElysiaJS: unit testing routes dengan Bun.test(), integration testing dengan supertest, E2E testing dengan running server, dan code coverage untuk memastikan kualitas kode.

Setelah di episode 16 kita memahami performance optimization, pada episode ini kita akan memahami testing — lapisan jaminan kualitas yang memastikan kode berfungsi sebagaimana mestinya dan tidak rusak saat ada perubahan.
Bun memiliki test runner bawaan yang sangat cepat:
import { describe, it, expect } from 'bun:test'
import { Elysia } from 'elysia'
const app = new Elysia()
.get('/', () => 'Hello Elysia!')
.get('/user/:id', ({ params }) => ({ id: params.id }))
describe('Routes', () => {
it('should return hello message', async () => {
const res = await app.handle(new Request('http://localhost/'))
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Elysia!')
})
it('should return user by id', async () => {
const res = await app.handle(new Request('http://localhost/user/123'))
expect(res.status).toBe(200)
const data = await res.json()
expect(data.id).toBe('123')
})
})app.handle() memungkinkan kalian menguji route tanpa menjalankan server — test menjadi sangat cepat.
it('should return 422 for invalid body', async () => {
const res = await app.handle(
new Request('http://localhost/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '' }),
})
)
expect(res.status).toBe(422)
})bun add -D supertest @types/supertestimport request from 'supertest'
describe('API Integration', () => {
it('should create and retrieve user', async () => {
const createRes = await request('http://localhost:3000')
.post('/users')
.send({ name: 'John', email: 'john@example.com' })
.expect(201)
const userId = createRes.body.id
const getRes = await request('http://localhost:3000')
.get(`/users/${userId}`)
.expect(200)
expect(getRes.body.name).toBe('John')
})
})Gunakan in-memory SQLite untuk testing:
import { Database } from 'bun:sqlite'
import { drizzle } from 'drizzle-orm/bun-sqlite'
function createTestDb() {
const sqlite = new Database(':memory:')
return drizzle(sqlite)
}describe('E2E Auth Flow', () => {
let server: subprocess
beforeAll(async () => {
server = Bun.spawn(['bun', 'run', 'src/index.ts'])
await Bun.sleep(1000) // wait for server
})
afterAll(() => server.kill())
it('should login and access protected route', async () => {
const loginRes = await fetch('http://localhost:3000/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'test@example.com', password: 'pass' }),
})
const { token } = await loginRes.json()
const profileRes = await fetch('http://localhost:3000/profile', {
headers: { Authorization: `Bearer ${token}` },
})
expect(profileRes.status).toBe(200)
})
})bun test --coverageCoverage report menunjukkan persentase kode yang teruji. Target: minimal 80% line coverage untuk kode produksi.
Tip
Mulai dengan unit testing untuk route handler, lalu tambahkan integration testing untuk alur yang melibatkan database, dan E2E testing untuk flow kritis seperti autentikasi.
Pada episode 17 ini, kalian telah memahami testing di ElysiaJS — dari unit test hingga E2E dan coverage.
Inti yang harus dibawa pulang:
app.handle() memungkinkan unit test tanpa running server.bun test --coverage untuk code coverage report.Di episode 18 selanjutnya kita akan memahami project structure dan scalability — struktur folder, modularisasi dengan plugin, scaling ke microservices, dan graceful shutdown. Sampai jumpa di episode 18!