來到測試的最後一個章節了,本篇要說明的是如何對 React-Router 做測試,確認 Component 在不同的 Router 的 Render 狀況,因此主要處理的部份在模擬 Router 的 History,用假的 History 觸發 Route Render Component。
為了輕鬆模擬出 history,我們可以使用 history 框架替我們處理 Url 的紀錄:
npm install history --save-dev
因為本篇會使用 Main
Component 進行測試,目前 Main
所在的位置是 src/index.jsx,但我們可以將 Main
抽出來,把它放到 src/Component 內,像是這樣子:
|-src
|-component
|-Main
|-index.js
|-Main.jsx
取出來後 src/component/Main/Main.jsx 的內容會變成:
同目錄下的 index.js 記得要將它 export。
另外抽出 Main
後,src/index.jsx 會只剩下 ReactDom.render
成為單純的進入點:
整理完後就能開始測試了!
第一步仍然是先建立測試檔案:
|-__tests__
|-component
|-Main
|-Main.test.js
完成測試的起手式做出來,本次要測試的內容為點擊待辦事項的連結後,首頁的 Content
是否會消失,然後 TodoList
有沒有正常 Render:
這裡大家需要注意一件事情,因為 Main
被 Render 後,在 Main
下的 TodoList
以及 Content
也都會被 Render 出來,這時候就會遇到一個問題,那就是他們兩個 Component 都被 Redux 罩著,所以即使 Main
沒使用到 Redux,還是得替它打造 Redux 環境:
然後其實在這裡從 todolist
中取出 Reducer 有點奇怪,怕大家混淆說明一下,因為目前專案裡也沒其他 Reducer,如果有使用 combileReducer
先將 Reducer 統一匯出,就會明瞭的多了,總之在測試環境中搭建 Redux 環境,總是取創建 store 的那個就對了!
處理完 Redux 後還不夠,當初使用 Router 的時候,也有在最外層放置 BrowserRouter
或 HashRouter
,但這裡輪不到他們出場,只需使用 Router
就行,它是前面兩種 Router
的底層接口,測試時會直接用它定義 history,除了 Router
外,也一併將 createMemoryHistory
從 history
中取出:
import { Router } from 'react-router-dom'
import { createMemoryHistory } from 'history'
接下來在 generateComponent
內使用 createMemoryHistory
創建一個假的 history,並將它用 Props 放到 Router
中,而 Router
的位置一樣是放在 Provider
的下一層,像這樣:
完成後可以先 Render 出來,確認目前被 Render 出來的 Component 是不是 Content
,Content
內的 data-testid
前幾篇已經添加過了了:
既然有了斷言,就能先進行測試看看:
看來畫面很正常,目前 Content
好好的被 Render 在畫面上,接下來要能夠取得待辦事項的連結,打開 Main
,並在兩個連結上添加 data-testid
:
加上 data-testid
後就可以用 fireEvent
點擊連結,以下先斷言點擊後 Content
是不是消失了:
執行測試後會發現出現一串錯誤,以下列出重點訊息:
錯誤訊息說明因為 Content
已經消失了,所以 getByTestId
找不到它就會報錯,要解決這個問題很容易,只需使用 queryByTestId
代替 getByTestId
就行了,因為 queryByTestId
只要找不到對應的 date-testid
則是會回傳 null,因此在斷言中使用 null 判斷有沒有 Render 就好,修改過後測試案例會變成:
測試也能正常通過,但這個測試案例的最終目標是去認 TodoList
是否有正常 Render,因此打開 src/component/TodoList/TodoList.jsxTodoList.jsx 加上 data-testid
:
最後就能在 Main_ClickTodoLisstLink_RenderTodoList 中斷言 TodoList
了,而其他的斷言避免過度指定,就可以先拿掉,最後測試案例會長這樣子,也好好地符合測試案例的名稱:
測試結果當然也是正常的:
本文的範例程式碼會提供在 GitHub 上,歡迎各位參考:)
關於 React 測試的所有技巧都已經在這幾個篇章內提到了,雖然還沒有全部都寫完,但是剩下的應該可以讓大家練習一下,如果不知道從何寫起,可以參考 npm run test-cov
產生的覆蓋率,如果在學習測試的過程中有遇到任何問題,都歡迎提出來討論。
最後也推薦 @testing-library/react 作者的 Blog,相信大家可以學到很多!
如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!
更新一下 卡在這邊有點久了
下方提出的缺陷後來發現其實是後面會加上 所以可以不用理
但是jest 在fireEvent上有出現BUG 但作者疑似太忙無法立即修復
所以建議先跳過最後一個test
src/component/Main/Main.jsx
檔案中缺少data-testid="contentBlock
import React from 'react';
import { Switch, Route, Link } from 'react-router-dom';
import Content from '../Content';
import TodoList from '../TodoList';
import List from '../List';
const Main = () => (
<div>
<ul>
<li>
<Link to="/" data-testid="homeLink">
首頁
</Link>
</li>
<li>
<Link to="/todolist" data-testid="todolistLink">
待辦事項
</Link>
</li>
</ul>
<Switch>
<Route exact path="/" component={Content} />
<Route path="/todolist" component={TodoList} />
<Route path="/list/:taskName" component={List} />
</Switch>
</div>
);
export default Main;
會導致斷言失敗
describe('Main',()=> {
test('Main_ClickTodoListLink_RenderTodoList',()=>{
const { getByTestId } = generateComponent(<Main />)
expect(getByTestId('contentBlock')).toBeInTheDocument()
})
})
感謝!我在把它補到正確的位置中!
話說我覺得你非常會除錯,然後我感覺很不好意思
看著我的文章辛苦了 XD
不用介意
既然實作了就會確保我每個commit都要可以pass
避免我之後的人再踩到