Bài viết này mình sẽ mang đến cho các bạn cái nhìn tổng quan về Jest, nếu các bạn chưa hiểu rõ về Unit Test thì nên đọc bài viết về Unit Test trước khi bắt đầu với Jest nhé.
1. Jest là gì?
Jest là một framework kiểm thử JavaScript tạo ra bởi Facebook, được thiết kế để làm cho việc viết kiểm thử trở nên dễ dàng. Ban đầu, Jest được tạo ra để kiểm thử các ứng dụng React, nhưng hiện nay được sử dụng để kiểm thử bất kỳ dự án Javascript nào.
2. TDD và BDD là gì?
Trước khi đi chi tiết vào Jest, thì mình muốn giới thiệu đến các phương pháp kiểm thử mà Jest hỗ trợ rất tốt: Test-Driven Development (TDD) và Behavior-Driven Development (BDD)
2.1 Test-Driven Development (TDD)
Test-Driven Development (TDD) là phương pháp phát triển phần mềm mà bài kiểm thử (tests) được viết trước khi viết code. TDD tập trung vào việc phát triển dựa trên yêu cầu về tính năng, và mỗi yêu cầu đó phải được kiểm thử cẩn thận để đảm bảo rằng nó được thực hiện đúng như mong đợi.
3 bước trong quy trình TDD được lặp đi lặp lại suốt quá trình phát triển phần mềm bao gồm:
- Red (Viết kiểm thử thất bại): bắt đầu bài kiểm thử không thành công (failing test). Bài kiểm thử này thể hiện yêu cầu mà mã cần thực hiện.
- Green (Viết mã để kiểm thử thành công): sau đó, viết mã tối thiểu cần thiết để bài kiểm thử thành công.
- Refactor (Cải tiến mã): cải tiến code, đảm bảo code sạch, tối ưu và vẫn giữ nguyên kết quả bài kiểm thử.
2.2 Behavior-Driven Development (BDD)
Behavior-Driven Development (BDD) là mở rộng của TDD, tập trung vào việc phát triển phần mềm dựa trên các hành vi mong đợi của hệ thống.
Trong BDD, các bài kiểm thử được viết theo giai đoạn (scenarios):
- Given: trạng thái ban đầu của hệ thống.
- When: hành động hoặc sự kiện diễn ra.
- Then: kết quả mong muốn sau khi hành động diễn ra.
Ví dụ với chức năng login cho bạn dễ hiểu hơn nhé:
- Given: xác định người dùng đang ở trang đăng nhập.
- When: xác định hành động mà người dùng thực hiện (nhập tên đăng nhập và mật khẩu hợp lệ).
- Then: xác định kết quả mong muốn (người dùng được chuyển hướng đến trang chủ).
3. Ưu và Nhược điểm của Jest
3.1 Ưu điểm của Jest
- Cấu hình đơn giản: jest không yêu cầu cấu hình phức tạp và có thể hoạt động ngay sau khi cài đặt (
zero config
). - Hiệu năng cao: jest hỗ trợ thực hiện kiểm thử song song, cải thiện hiệu suất, đặc biệt đối với các dự án lớn.
- Snapshot Testing: đảm bảo không có sự thay đổi bất ngờ nào xảy ra.
- Mocking: hỗ trợ tích hợp tốt cho việc mô phỏng các module và hàm, tách biệt các phần cần kiểm thử.
- Code Coverage: tích hợp đo lường để đảm bảo rằng mã của bạn được kiểm thử đầy đủ.
- Tích hợp với CI/CD: Dễ dàng tích hợp với Jenkins, GitHub Actions,...
3.2 Nhược điểm của Jest
- Nếu không được tối ưu hoá có thể sẽ chậm hơn đối với các dự án rất lớn: chú ý trong việc cấu hình để đảm bảo các bài kiểm thử bị chạy lại không cần thiết.
- Khả năng tương thích: mặc dù Jest hỗ trợ nhiều công cụ, nhưng đôi khi sẽ xảy ra xung đột với các thư viện khác, đặc biệt là khi bạn sử dụng các framework hoặc công cụ không phổ biến.
- Snapshot Testing có thể gây phiền phức: bảo trì snapshot có thể trở nên phức tạp nếu bạn không được quản lý tốt.
4. Tổng quan về các tính năng của Jest
4.1 Using Matchers
Dùng để kiểm tra giá trị mong muốn với giá trị hàm thực hiện.
test('two plus two is four', () => {
expect(2 + 2).toBe(4); // Kiểm tra phép tính 2 + 2 có bằng 4 không
});
test('object assignment', () => {
const data: { one: number; two?: number } = { one: 1 };
data['two'] = 2;
expect(data).toEqual({ one: 1, two: 2 }); //Kiểm tra trường của object có giống nhau không
});
4.2 Test Truthiness (Kiểm tra tính đúng đắn)
Khi bạn cần phân biệt các giá trị false
, null
, undefined
để có thể đưa ra cách xử lý phù hợp.
test('null', () => {
const n: null = null;
expect(n).toBeNull(); // Kiểm tra biến n là null.
expect(n).toBeDefined(); // Kiểm tra biến n được định nghĩa.
expect(n).not.toBeUndefined(); // Kiểm tra biến n không phải undefined.
expect(n).not.toBeTruthy(); // Kiểm tra biến n không phải là truthy.
expect(n).toBeFalsy(); // Kiểm tra biến n là falsy.
});
4.3 Number
Dùng để so sánh các con số với các điều kiện như lớn hơn, nhỏ hơn, bằng nhau hoặc không bằng nhau.
test('two plus two', () => {
const value: number = 2 + 2;
expect(value).toBeGreaterThan(3); // Kiểm tra giá trị lớn hơn 3.
expect(value).toBeLessThanOrEqual(4); // Kiểm tra giá trị nhỏ hơn hoặc bằng 4.
expect(value).toBe(4); // Kiểm tra giá trị bằng 4.
});
4.4 String
Dùng để kiểm tra có hoặc không có trong chuỗi
test('there is no I in team', () => {
expect('team').not.toMatch(/I/); // Kiểm tra chuỗi "team" không chứa ký tự "I" không.
});
4.5 Array
Dùng để kiểm tra xem mảng có chứa phần tử không
test('the shopping list has beer on it', () => {
const shoppingList: string[] = ['beer', 'milk', 'bread'];
expect(shoppingList).toContain('beer'); // Kiểm tra mảng có chứa phần tử 'beer' không.
});
4.6 Exception
Dùng để kiểm tra xem function có ném ra exception không, nội dung của exception đó
function compileAndroidCode(): never {
throw new Error('200Lab');
}
test('compiling android goes as expected', () => {
expect(() => compileAndroidCode()).toThrow('200Lab'); // Kiểm tra exception
});
4.7 Callbacks
Kiểm thử các function bất đồng bộ thông qua callbacks
function fetchData(callback: (data: string) => void): void {
callback('peanut butter');
}
test('the data is peanut butter', (done: jest.DoneCallback) => {
function callback(data: string) {
expect(data).toBe('peanut butter');
done(); // Gọi `done` để kết thúc kiểm thử khi callback được gọi.
}
fetchData(callback);
});
4.8 Promise
Dùng để kiểm thử các function bất đồng bộ trả về Promise
function fetchData(): Promise<string> {
return Promise.resolve('peanut butter');
}
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter'); // Kiểm tra dữ liệu trả về từ promise.
});
});
4.9 Async/Await
Sử dụng cú pháp async
/ await
để kiểm thử code bất đồng bộ
async function fetchData(): Promise<string> {
return 'peanut butter';
}
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter'); // Kiểm tra giá trị trả về từ hàm không đồng bộ.
});
4.10 Mock Function
Dùng để kiểm thử hàm và các giá trị trả về
const myMock = jest.fn().mockReturnValue('default'); // Tạo một hàm mock với giá trị trả về mặc định.
test('mock function returns default value', () => {
expect(myMock()).toBe('default');
});
4.11 Mock Implementations
Jest cho phép mô phỏng toàn bộ triển khai của một hàm
const myMock = jest.fn((x: number, y: number) => x + y); // Triển khai hàm mock tùy chỉnh.
test('mock implementation of addition', () => {
expect(myMock(1, 2)).toBe(3);
});
4.12 Testing Snapshot
Giúp đảm bảo giao diện hoặc kết quả của function không thay đổi ngoài ý muốn
test('snapshot', () => {
const user = { name: 'John', age: 30 };
expect(user).toMatchSnapshot(); // Tạo snapshot và so sánh
});
4.13 Tổ chức bài kiểm thử theo nhóm
Sử dụng describe
để tổ chức các bài kiểm thử theo nhóm
describe('matching cities to foods', () => {
test('Vancouver has sushi', () => {
expect(true).toBe(true);
});
test('Toronto has poutine', () => {
expect(true).toBe(true);
});
});
4.14 Thiết lập và dọn dẹp môi trường
Dùng beforeAll
và afterAll
để thiết lập, dọn dẹp môi trường một lần cho tất cả các bài kiểm thử.
beforeAll(() => {
console.log('Setup before all tests');
});
afterAll(() => {
console.log('Clean up after all tests');
});
Hoặc bạn cũng có thể sử dụng beforeEach
và afterEach
đối với mỗi bài kiểm thử
beforeEach(() => {
console.log('Setup before each test');
});
afterEach(() => {
console.log('Clean up after each test');
});
5. Hướng dẫn config Jest trong dự án Typescript
5.1 Khởi tạo dự án Typescript
npm init -y
npm install ts-node typescript --save-dev
npx tsc --init
Đây là file tsconfig.json
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"exclude": ["node_modules"]
}
5.2 Cài đặt Jest
npm install jest @types/jest ts-jest --save-dev
- jest: thư viện chính của Jest - trình chạy test
- @types/jest: định nghĩa kiểu cho Jest
- ts-jest: tích hợp Jest cho Typescript
5.3 Cấu hình Jest
Sau khi hoàn thành việc cài đặt các thư viện cần thiết, tiến hành tạo một tệp mới jest.config.ts
ở thư mục gốc của dự án.
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
moduleFileExtensions: ['js', 'ts'],
};
export default config;
Sau đó, tương tự với câu lệnh build bạn hãy vào lại file package.json
để thêm câu lệnh "test":"jest"
vào phần script nha.
{
"name": "200lab_jest_demo",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
"scripts": {
"build": "tsc",
"test": "jest"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
}
}
5.4 Tạo mới Test case
Đầu tiên, bạn tiến hành tạo file src/math.test.ts
và file src/math.ts
.
- src/math.ts
export function add(a: number, b: number): number {
return a + b;
}
- src/math.test.ts
import { add } from './math';
describe('Math functions', () => {
test('should return 5 for add(2, 3)', () => {
expect(add(2, 3)).toBe(5);
});
test('should return 0 for add(-2, 2)', () => {
expect(add(-2, 2)).toBe(0);
});
});
Cuối cùng, hãy chạy thử npm test
và xem kết quả trên terminal
6. Kết luận
Qua bài viết này, hy vọng bạn sẽ hiểu hơn về Jest, hiểu biết cơ bản về việc sử dụng Jest trong dự án Typescript của mình. Trong các dự án lớn, bạn có thể tích hợp thêm husky hoặc eslint.
Một vài chủ đề thú vị có thể bạn quan tâm:
Bài viết liên quan
Hướng dẫn tích hợp Redux và React Query trong dự án React Vite
Nov 22, 2024 • 8 min read
Giới thiệu Kiến trúc Backend for Frontend (BFF)
Nov 16, 2024 • 10 min read
Flask là gì? Hướng dẫn tạo Ứng dụng Web với Flask
Nov 15, 2024 • 7 min read
Webhook là gì? So sánh Webhook và API
Nov 15, 2024 • 8 min read
Spring Boot là gì? Hướng dẫn Khởi tạo Project Spring Boot với Docker
Nov 14, 2024 • 6 min read
Two-Factor Authentication (2FA) là gì? Vì sao chỉ Mật khẩu thôi là chưa đủ?
Nov 13, 2024 • 7 min read