我們現在試著用 TDD 的方式來開發一個表單。
這個表單主要功能是發表文章,它接收標題 (title) 、內容 (content)、標籤 (tag),並且有一個按鈕可以點擊送出表單,並發出 HTTP request。
我們會經歷以下步驟,一步一步先寫測試再寫程式,重複亮紅燈再亮綠燈的循環過程:
發送資料到 API 後,要將使用者導回首頁。我們將用 react-router 的 <Redirect />
component 來實作導頁功能。
首先,一樣從寫測試開始,先到 __tests__/post-editor.js
檔案加上測試:
'react-router'
裡的 Redirect
,並給它一個 alias MockRedirect
,方便維護測試檔案時清楚辨識這是 mock 版本的 Redirect
。'react-router'
。使用 jest.fn()
mock Redirect
,因為 Redirect
的 mock function 是什麼對這個測試本身來說並不重要,所以傳入 () => null
作為 mock function 即可。tests/post-editor.js
import {Redirect as MockRedirect} from 'react-router'
import {Editor} from '../post-editor'
jest.mock('react-router', () => {
return {
Redirect: jest.fn(() => null),
}
})
Redirect
是否傳入正確的參數,以及是否被呼叫一次。expect(MockRedirect).toHaveBeenCalledWith({to: '/'}, {}))
expect(MockRedirect).toHaveBeenCalledTimes(1)
這時候,測試是 fail ❌。
回到 post-editor.js
補上功能:
'react-router'
裡的 <Redirect />
。redirect
state,初始值為 false
。當 savePost()
完成之後 setRedirect(true)
。redirect
如果為 true
,則 return <Redirect to="/" />
。post-editor.js
import {Redirect} from 'react-router'
function Editor({user}) {
const [isSaving, setIsSaving] = React.useState(false)
const [redirect, setRedirect] = React.useState(false)
function handleSubmit(e) {
e.preventDefault()
const {title, content, tags} = e.target.elements
const newPost = {
...
}
setIsSaving(true)
savePost(newPost).then(() => setRedirect(true))
}
if (redirect) {
return <Redirect to="/" />
}
return (
...
)
}
預期此時測試應該要亮綠燈 ✅,但卻是 fail ❌?
console output 顯示 MockRedirect
並沒有被預期的參數呼叫。
這是因為 savePost()
是非同步的,當 promise resolve 之後才呼叫 <Redirect />
。但我們剛剛寫的 assertions 都是同步的,才造成錯誤。現在,我們必須等到 MockRedirect
確實被呼叫:
wait()
等待tests/post-editor.js
test('renders a form with title, content, tags, and a submit button', async () => {
...
await wait(() => expect(MockRedirect).toHaveBeenCalledWith({to: '/'}, {}))
expect(MockRedirect).toHaveBeenCalledTimes(1)
})
測試 pass 亮綠燈 ✅。
...未完