Day 26 测试 React 元件:使用 React Testing Library 体验 Test Driven Development (TDD) - 6

前面几天我们已经用 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 )、 submitButtonfakeUserfakePost

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

30天打造品牌特色电商网站 Day.18 文字的样子

之前曾在 Day8 有跟大家提过字体常用的样式,今天来带大家深入探讨各式文字怎麽设计,才会带给使用者...

MacOS读取蓝牙摇杆讯号,利用python修改pynput程序码实现 - 3.修改pynput

请参考之前的文章:第二篇 5. 修改pynput if event_type == Quartz.N...

[Day 25] - 『转职工作的Lessons learned』 - Cube.js(I)

今天要介绍一下工作上有使用的到的另一项工具 - Cube.js。 Cube.js 是一个开源的API...

[Day3] Android - Kotlin笔记:高阶函式与 lambda

来理解一下lambda 以下简单介绍lambda的演进 我们一般写一个function: fun s...

Day8:原来机器学习这个词跟我想的不太一样

  这几天研究下来,发现有三个词汇很让人搞不懂,也就是人工智慧(Artifical Intellig...