前面几天我们已经用 TDD 的方式完成了 <Editor />
元件,但不要忘了 TDD 中很重要的「重构」部分。在 __tests__/post-editor.js
的两个测试中有重复的逻辑,这些重复的逻辑对於未来的维护来说会增加不必要的负担,不易分辨每个测试之间的差别是什麽。现在我们要来重构这些重复的程序码,让每个测试之间的关注点更明确。
下面是 __tests__/post-editor.js
目前的两个测试:
tests/post-editor.js
test('renders a form with title, content, tags, and a submit button', async () => {
mockSavePost.mockResolvedValueOnce()
const fakeUser = userBuilder()
const {getByLabelText, getByText} = render(<Editor user={fakeUser} />)
const fakePost = postBuilder()
getByLabelText(/title/i).value = fakePost.title
getByLabelText(/content/i).value = fakePost.content
getByLabelText(/tags/i).value = fakePost.tags.join(', ')
const submitButton = getByText(/submit/i)
fireEvent.click(submitButton)
expect(submitButton).toBeDisabled()
expect(mockSavePost).toHaveBeenCalledWith({
...fakePost,
authorId: fakeUser.id,
})
expect(mockSavePost).toHaveBeenCalledTimes(1)
await wait(() => expect(MockRedirect).toHaveBeenCalledWith({to: '/'}, {}))
})
test('renders an error message from the server', async () => {
const testError = 'test error'
mockSavePost.mockRejectedValueOnce({data: {error: testError}})
const fakeUser = userBuilder()
const {getByText, findByRole} = render(<Editor user={fakeUser} />)
const submitButton = getByText(/submit/i)
fireEvent.click(submitButton)
const postError = await findByRole('alert')
expect(postError).toHaveTextContent(testError)
expect(submitButton).not.toBeDisabled()
})
可以发现下面这几行是两个测试中重复出现的逻辑:
const fakeUser = userBuilder()
const {getByLabelText, getByText} = render(<Editor user={fakeUser} />)
const fakePost = postBuilder()
getByLabelText(/title/i).value = fakePost.title
getByLabelText(/content/i).value = fakePost.content
getByLabelText(/tags/i).value = fakePost.tags.join(', ')
const submitButton = getByText(/submit/i)
新增一个 function renderEditor
,将重复的程序码抽到这个 funtion。将 render()
method 回传的结果宣告为 utils
变数。renderEditor
回传一个 object,里面有展开後的 utils 属性( ...utils
)、 submitButton
、 fakeUser
、 fakePost
。
tests/post-editor.js
function renderEditor() {
const fakeUser = userBuilder()
const utils = render(<Editor user={fakeUser} />)
const fakePost = postBuilder()
utils.getByLabelText(/title/i).value = fakePost.title
utils.getByLabelText(/content/i).value = fakePost.content
utils.getByLabelText(/tags/i).value = fakePost.tags.join(', ')
const submitButton = utils.getByText(/submit/i)
return {
...utils,
submitButton,
fakeUser,
fakePost,
}
}
第一个测试原本被抽出的程序码,现在可以取代为 const {submitButton, fakePost, fakeUser} = renderEditor()
:
test('renders a form with title, content, tags, and a submit button', async () => {
mockSavePost.mockResolvedValueOnce()
const {submitButton, fakePost, fakeUser} = renderEditor()
fireEvent.click(submitButton)
expect(submitButton).toBeDisabled()
expect(mockSavePost).toHaveBeenCalledWith({
...fakePost,
authorId: fakeUser.id,
})
...
}
第二个测试原本被抽出的程序码,现在可以取代为 const {submitButton, findByRole} = renderEditor()
:
test('renders an error message from the server', async () => {
const testError = 'test error'
mockSavePost.mockRejectedValueOnce({data: {error: testError}})
const {submitButton, findByRole} = renderEditor()
fireEvent.click(submitButton)
const postError = await findByRole('alert')
expect(postError).toHaveTextContent(testError)
expect(submitButton).not.toBeDisabled()
})
重构後,测试亮绿灯 ✅,代表没问题。
<<: Day 27 - ROS 树莓派光达履带小车实作 (1)
>>: Day 26 Explore monitoring and reporting
之前曾在 Day8 有跟大家提过字体常用的样式,今天来带大家深入探讨各式文字怎麽设计,才会带给使用者...
请参考之前的文章:第二篇 5. 修改pynput if event_type == Quartz.N...
今天要介绍一下工作上有使用的到的另一项工具 - Cube.js。 Cube.js 是一个开源的API...
来理解一下lambda 以下简单介绍lambda的演进 我们一般写一个function: fun s...
这几天研究下来,发现有三个词汇很让人搞不懂,也就是人工智慧(Artifical Intellig...