Devahoy Logo
PublishedAt

React

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

เขียน 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 ได้เลย

1
import { defineConfig } from 'vite'
2
import react from '@vitejs/plugin-react-swc'
3
4
// https://vitejs.dev/config/
5
export default defineConfig({
6
plugins: [react()],
7
test: {
8
globals: true
9
}
10
})

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

vite.config.ts
1
/// <reference types="vitest" />
2
import { defineConfig } from 'vite'
3
import react from '@vitejs/plugin-react-swc'
4
5
// https://vitejs.dev/config/
6
export default defineConfig({
7
plugins: [react()],
8
test: {
9
globals: true
10
}
11
})

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

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

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

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

1
import { describe, it, expect } from 'vitest'
2
3
describe('something', () => {
4
it('should work'), () => {
5
expect(true).toEqual(true)
6
})
7
})

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

1
{
2
"compilerOptions": {
3
"types": ["vitest/globals"]
4
}
5
}

การ 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)
1
import { defineConfig } from 'vitest/config'
2
3
export default defineConfig({
4
test: {
5
// ...
6
}
7
})

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

1
{
2
"scripts": {
3
"test": "vitest"
4
}
5
}

เขียนเทสแรก

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

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

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

1
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 แบบนี้

1
{
2
"scripts": {
3
"test": "vitest",
4
"test:coverage": "vitest --coverage",
5
"test:ui": "vitest --ui"
6
}
7
}

ผลลัพธ์เมื่อรัน 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

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts