Hướng dẫn viết unit test React với Jest và Enzyme (P1)
1. Front-end unit test
- Dạo quanh 1 vòng blog của công ty thì ta thấy các vấn đề chủ yếu xoay quanh về
coding
là chính,unit testing
đã ít,front-end unit testing
còn ít hơn. - Hôm nay cùng tìm hiểu về
unit testing
- cụ thể là áp dụng Jest và Enzyme để thực hiện unit test cho ReactJS.
1.1. Jest
- Jest được tạo bởi facebook và là 1 frameword dùng để test javascript.
- Jest là 1 cái tên quen thuộc, thường được cài đặt sẵn vào khi tạo cả project reactjs lẫn project react-native nghĩa là chúng ta không cần cài đặt hoặc config gì thêm nữa và đặc biệt nó hiện đang có 35.3k sao.
- Jest đơn giản và dễ dàng sử dụng, document rõ ràng cụ thể. Các bạn có thể tham khảo thêm ở đây
1.2. Enzyme
- Enzyme - thư viện được phát triển bởi nhà Airbnb
- Lí do sử dụng enzyme:
- API của Enzyme có nghĩa là trực quan và linh hoạt bằng cách bắt chước API của jQuery để thao tác và truyền tải DOM.
- Có
shallow rendering
vàmount rendering
. Bạn có thể tìm hiểu thêm.
ở đây. Bạn có thể hiểu làshallow rendering
dùng khi mà bạn không muốn render componet con và khi bạn muốn render cả component con thì bạn dùngmount rendering
- Có 19.6k sao trên github.
2. Set up a React application
- Trước hết thì bạn cần Node 6+ và yarn hoặc npm được cài đặt trên máy bạn.
Bước 1: Tạo 1 project
npx create-react-app counter-app
- Như đã nói trên thì khi khởi tạo project thì cài luôn
jest
vào project và có thể chạy yarn test
Bước 2: Cài đặt và config enzyme
yarn add enzyme enzyme-adapter-react-16 --dev
- Ta cần tải thêm adapter tương ứng với phiên bản react mình đang sử dụng. Nếu bạn sử dụng react 17 thì dùng
yarn add @wojtekmaj/enzyme-adapter-react-17
- Thêm config bằng cách sửa file
setupTests.js
. Việc để chúng ta báo vs jest và enzyme rằng chúng ta sẽ sử dụng adapter nào.
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
-
Với create-react-app thì đã cài đặt sẵn sẽ chạy file này trước bất kỳ test nào.
-
Sửa file App.js
import React, { useState } from 'react';
const App = () => {
const [counter, setcounter] = useState(0);
return (
<div>
<h1>This is counter app</h1>
<div className="counter-value">Count: {counter}</div>
<button className="increment" onClick={() => setcounter(counter + 1)}>
Increment
</button>
<button className="decrement" onClick={() => setcounter(counter - 1)}>
Decrement
</button>
</div>
);
}
export default App;
Bước 3: Test
- Chúng ta đến
src/App.test.js
và sửa
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
describe('App component', () => {
//Test 1: Ví dụ test quản lý state cơ bản
it('starts with a count of 0', () => {
const wrapper = shallow(<App />);
const text = wrapper.find('div.counter-value').text();
expect(text).toEqual('Count: 0');
});
//Test 2: Ví dụ test tương tác của người dùng
it('increments count by 1 when the increment button is clicked', () => {
const wrapper = shallow(<App />);
const incrementBtn = wrapper.find('button.increment');
incrementBtn.simulate('click');
const text = wrapper.find('div.counter-value').text();
expect(text).toEqual('Count: 1');
});
In the above snippet, the shallow render of our App component is stored in the wrapper variable. We then grab the text inside the p tag within the component’s output and check if the text is the same was what we passed into the toEqual matcher function.
-
Đoạn Test 1 chúng ta thấy,
shallow render
của App component được lưu lại vào 1 wrapper variable. Rồi dùng API của enzyme để tìmdiv
có classname làcounter-value
và lấy text ra so sánh bằng hàmtoEqual
. -
Đoạn Test 2 khá giống đoạn Test 1 nhưng có thêm
incrementBtn.simulate('click');
.simulate()
function có tác dụng mô phỏng lại 1 số DOM event và ở đây mình đã mô phỏng lạiclick
và state bây giờ sẽ là 1. -
Khi chạy
yarn test
thì chúng ta có kết quả thế này
-Nhưng để ý ở đây chúng ta đang gọi const wrapper = shallow(<App />);
2 lần mà nguyên tắc nếu gọi quá 1 lần thì dùng biến chung. Chúng ta sửa đoạn code như sau.
...
describe('App component', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<App />);
});
...
-
Và tương tự cũng sẽ
afterEach
. -
Tiếp theo ta sẽ xem qua về việc test react component bằng snapshot, 1 chức năng của jest.
Snapshots testing
Snapshot testing
giúp chúng ta có thể kiểm tra được kết quả các lần render của 1 component là luôn giống nhau. Khi chạy lần đầu Jest sẽ render React component đang được test và lưu output lại trong 1 file JSON và ở các lần sau Jest sẽ check xem rằng cái output của component có khác với cái trước không- Để sử dụng chức năng snapshot của jest của Chúng ta cài thêm react-test-renderer
yarn add react-test-renderer --dev
- Thêm import vào đầu file
import renderer from 'react-test-renderer';
- Và thêm 1 test dưới:
it('matches the snapshot', () => {
const tree = renderer.create(<App />).toJSON();
expect(tree).toMatchSnapshot();
});
- Sau khi chạy xong lần đầu thì sẽ tạo ra 1 folder snapshots trong
src
như thế này
- Nếu lần sau chạy thành công thì nó sẽ hiển thị.
- Nếu chúng ta có sửa 1 dòng code thì không thành công nó sẽ báo lỗi.
- Nhưng nếu mình chủ động sửa và muốn update thì hãy ấn
u
để update lại snapshot.
3. Kết luận
- Mình đã áp dụng Jest vs Enzyme vào dự án thì khá là dễ sử dụng.
- Nhưng mình phát hiện ra rằng điều khó ở đây không phải viết unit-test như nào mà là lên kịch bản viết unit-test như nào để có thể phủ rộng nhất có thể. Thì mình hi vọng rằng bản thân trong thời gian sau có đủ hiểu biết để viết 1 blog nói về vấn đề lên kịch bản này.