這回在初步撰寫 React Unit Testing 的內容時,有一些思考盲區產生。這邊趁印象深刻之際,將其筆記下來。
內容
測試的框架是使用 React Testing Library
常見方法
render:會將測試的 Component 的節點都渲染出來。render 後,會有一系列的方法可以使用。
screen:可以取得 render 後的物件內容,並可直接用 screen.<方法/內容> 來使用一些功能。如 getByTestId:用來取得你在 DOM 上標明的 data-testid
container:整包 render 的內容,可以用來印出全部的 innerHTML,但我會傾向於使用 screen.debug() 來取代
fireEvent:用來觸發按鈕或是滑鼠的行為
waitFor:當你使用 fireEvent 後,會需要透過 act 來進行 rerender。若你是用 waitFor 的話,那這段會被自動執行,無需自行撰寫
心法
1. useState 的 hook 是沒有辦法 mock 的。若你的元件接收 useState 的內容的話,那你可用宣告一個變數,並用 jest.fn() 將方法傳入。最後你就可以監聽你的方法有沒有被觸發,同時值有沒有被改動
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import { Updater } from '@tanstack/react-table'; export interface PaginationProps { isLoading?: boolean; totalPage: number; pageIndex: number; pageSize: number; pageSizeOptions: number[]; setPageSize: (updater: Updater<number>) => void; onPageChange: (page: number) => void; } describe('Pagination', () => { let props: PaginationProps; let mockPageSize: number; let mockPageIndex: number; beforeEach(() => { mockPageIndex = 2; const onPageChangeMock = jest.fn((index: number) => (mockPageIndex = index)); const setPageSizeMock = jest.fn((size: number | ((old: number) => number)) => { mockPageSize = typeof size === 'number' ? size : size(mockPageSize); }); props = { totalPage: 10, pageIndex: mockPageIndex, pageSize: mockPageSize, pageSizeOptions: [10, 20, 30, 50], setPageSize: setPageSizeMock, onPageChange: onPageChangeMock, }; }); describe('Functional Test', () => { it('should change to the next page', async () => { const { getByTestId } = render(<Pagination {...props} />); const nextButton = getByTestId('next-page-button'); fireEvent.click(nextButton); await waitFor(() => { expect(props.onPageChange).toBeCalledTimes(1); expect(mockPageIndex).toBe(3); }); }); it('should change to the previous page', async () => { const { getByTestId } = render(<Pagination {...props} />); const previousButton = getByTestId('previous-page-button'); fireEvent.click(previousButton); await waitFor(() => { expect(props.onPageChange).toBeCalledTimes(1); expect(mockPageIndex).toBe(1); }); }); }); }); |
2. 元件若接收 useState 所傳入的值,那在測試階段的 state 是不會變的。換言之,你無法透過取得元件內的值狀態值來當做判斷。
3. 若你不曉得該怎麼測試某些元件庫的元件,那可以參照元件庫的原始碼,裡面多半都是有 Unit Testing 的部份。以 Chakra UI 為例子, Tooltip 元件的單元測試寫法就在此。
4. queryBy:找不到時不會噴錯,常用於「檢查某個元素在不在 DOM 」
5. findBy:會需要搭配 async/await,一般來說是一開始並不會在渲染在畫面上的
6. getBy:都可以用,但找不到時會直接丟出 Error
參考資料
1. Day19 | Component 的測試方式不私藏
2. components/tooltip/tests/tooltip.test.tsx
3. Testing Library – About Queries