NEW

เขียน Unit Test ด้วย React + TypeScript + Vitest

PublishedAt

React

Tutorial

เขียน Unit Test ด้วย React + TypeScript + Vitest

ปกติโปรเจ็ค React ส่วนใหญ่จะนิยมใช้ Jest + Testing Library กันใช่มั้ย แต่ช่วงนี้ผมได้ลองใช้ Vitest แล้วรู้สึกชอบมากกว่า ก็เลยพยายามเปลี่ยนมาใช้ Vitest (เอามาแทนที่ Jest) วันนี้เลยมาเขียนบล็อกแนะนำการ ติดตั้ง การตั้งค่า Vitest สำหรับโปรเจ็ค React.js กันครับ

โดยตัวอย่างโปรเจ็ค ผมจะใช้เป็น default template นะครับ

  • React + TypeScript ด้วยการใช้ Vite
  • Vitest
Vite
Next Generation Frontend Toolingvite.dev
Vitest
Next generation testing framework powered by Vitevitest.dev

สร้างโปรเจ็ค

เริ่มต้น ผมสร้าง default template โดยการใช้ Vite (จริงๆ Vitest ใช้ได้เกือบทุก library/framework นะครับ)

Terminal window
npm create vite@latest hello-vitest -- --template react-ts

เราก็จะได้ Project React + Vite มาแบบง่ายๆ 1 เว็บ

ติดตั้ง Vitest

การติดตั้ง Vitest คือ

Terminal window
npm install -D vitest

การ Config Vitest

โดยปกติ ถ้าโปรเจ็คเราเป็น Vite ตัว Vitest จะอ่าน config จาก vite.config.ts ได้เลย

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true
}
})

แต่ว่าการ config vitest ในไฟล์ config ของ vite นั้น ตัว TypeScript มันไม่รู้จัก เราต้องบอกให้มัน reference ไปที่ Vitest Type ครับ คือเพิ่ม reference type ที่ส่วน top (ใช้ triple slash) จะได้เป็นแบบนี้

vite.config.ts
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true
}
})

หรืออีกตัวเลือกคือ ไม่ใช้ triple slash แต่เราก็เปลี่ยน defineConfig จาก vitest/config แทนที่ vite แบบนี้

import { defineConfig } from 'vite'
import { defineConfig } from 'vitest/config'

ส่วนที่ผมกำหนด globals: true คือ ทำให้เราสามารถใช้ describe, it test ได้แบบ global เหมือนกับ Jest (ข้อดีคือเหมาะสำหรับคนที่ migrate จาก Jest ไม่ต้องแก้อะไรมาก)

แต่ถ้าใช้ global เป็น false (default) เวลาเขียนเทส ก็จะ import จาก vitest ไม่ใช่ global แล้ว แบบนี้

import { describe, it, expect } from 'vitest'
describe('something', () => {
it('should work'), () => {
expect(true).toEqual(true)
})
})

ทำการเพิ่ม Compiler Options ที่ไฟล์ tsconfig.json เพื่อให้ TypeScript รู้จัก Vitest globals (ไม่งั้นตัว VS Code อาจจะฟ้อง error)

{
"compilerOptions": {
"types": ["vitest/globals"]
}
}

การ Config ด้วย vitest.config.ts

นอกจากการ Config ผ่าน Vite Config แล้ว เราสามารถ config ด้วย Vitest เอง คือไฟล์ vitest.config.ts การ config แบบนี้ เหมาะสำหรับโปรเจ็คที่เราไม่ได้ใช้ Vite นั่นเอง

  • ตัว vitest.config.ts จะเป็นมี priority สูงกว่า คือถ้ามีไฟล์ vitest กับ vite ตัว config จะอ่าน vitest.config.ts ก่อน อ่านไฟล์ vite.config.ts
  • ตัวอย่าง config (เหมือนกับ vite.config.ts)
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// ...
}
})

เพิ่ม script test ใน package.json

{
"scripts": {
"test": "vitest"
}
}

เขียนเทสแรก

ลองเขียน test ขึ้นมาไฟล์นึง สมมติ ผมจะทำ helpers function ซักตัว เกี่ยวกับการ calculate ละกัน ผมตั้งชื่อว่า calculate.spec.ts ในโฟลเดอร์ src/helpers

import { add } from './calculate'
describe('add', () => {
it('should add two numbers', () => {
expect(add(1, 2)).toBe(3)
})
})

และไฟล์ calculate.ts แบบนี้

export const add = (a: number, b: number): number => a + b

ปกติ default ถ้าเราไม่ได้ config อะไร ตัว vitest จะ scan หาไฟล์ _.spec หรือ _.test และรันเทสให้เราครับ

ลองรันเทสดู

Terminal window
npm test

จะได้ผลลัพธ์ประมาณนี้

Terminal window
src/helpers/calculate.spec.ts (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 15:40:14
Duration 11ms

ตัว vitest จะเข้าโหมด watch ให้เราเอง เวลาเราแก้ไขไฟล์ ตัว test ก็จะทำการ re-run ใหม่ครับ สะดวกมาก (กด q เพื่อออกจากโหมดเทส)

Test Coverage

ตัว default coverage ของ Vitest คือ v8 ถ้าเราไม่ได้ config ว่าจะให้เป็น reporter provider อะไร แต่ถ้าเราจะใช้ coverage ยังไงก็ต้องติดตั้ง package ก่อน

Terminal window
npm install -D @vitest/coverage-v8

(กรณีไม่ได้ติดตั้ง แล้วเราสั่งรันเทสด้วย coverage ตัว Vitest จะ detect และถามเราว่า ต้องการติดตั้ง coverage-v8 หรือไม่ ก็เลือกติดตั้งวิธีนี้ก็ได้เช่นกัน)

ทีนี้เวลาเราจะเทสด้วย coverage ก็ใส่ option ลงไป แบบนี้

Terminal window
vitest --coverage

Vitest UI

ติดตั้ง Vitest UI เพื่อให้เราสามารถรันโหมด UI ได้ ข้อดีคือ ดูง่าย สามารถดูเทส รันเทส ผ่าน Browser ได้เลย

Terminal window
npm install -D @vitest/ui

จากนั้นเพิ่ม script สำหรับ ui ต่อจาก coverage แบบนี้

{
"scripts": {
"test": "vitest",
"test:coverage": "vitest --coverage",
"test:ui": "vitest --ui"
}
}

ผลลัพธ์เมื่อรัน coverage

Terminal window
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 14:08:25
Duration 189ms (transform 21ms, setup 0ms, collect 9ms, tests 2ms, environment 0ms, prepare 58ms)
% Coverage report from v8
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
calculate.ts | 100 | 100 | 100 | 100 |
--------------|---------|----------|---------|---------|-------------------

ผลลัพธ์เมื่อรัน vitest ui

Vitest UI

สรุป

บทความนี้ก็เป็นวิธีการตั้งค่า Vitest แบบง่ายๆนะครับ ลองทดลองเล่นกันดู เขียนเทสเพิ่ม ลองรัน รวมถึง ตัว environment default คือ node.js เราสามารถเทสด้วย jsdom หรือ happy-dom ได้ เพื่อให้ environment เป็น web ครับ ลองดูนะครับ เดี๋ยวบทความหน้าจะเขียนเรื่องการเทสตัว React Component กันครับ

Happy Coding ❤️

Reference

Vitest
Next generation testing framework powered by Vitevitest.dev
Authors
avatar

Chai Phonbopit

Senior Software Engineer ประสบการณ์กว่า 12 ปี ด้าน Frontend: React, Next.js, Tailwind CSS และ Backend: Node.js, Express, NestJS ปัจจุบันสนใจ Astro, Cloudflare Workers และ AI Coding Tool