# 任务 2：测试驱动组件单元接口

Hey，你好，我是 JimmyLv 吕靖，这是你开始 前端 TDD 练习的第 12 天，今天我们会以 CommentBox 项目作为练习，学习如何由外到内驱动出组件树的拆分，以及如何通过 Testing Library 测试驱动开发组件的接口，面向接口编程。

## 练习目标

### 功能 3：嵌套评论

评论可以嵌套，并且可以再次回复，保留 2 个层级，或可以支持无限层级的嵌套。

### 功能 4：回复框

回复评论框组件需要相同样式，想一想怎么复用呢？当然也需要支持自定义回复框。

## 本次任务的练习要求

* 首先，提取已完成的基础评论框和留言列表组件（如果你还没有拆分组件的话）
* 使用 Testing Library 为上述组件再次编写行为测试
* 新功能必须先写 Cypress E2E 测试，再写组件级别测试

按照以下手法提取组件：

![](/files/-M1VmJxbD_gXNi1F6f8o)

## 组件化与 UI 测试

在组件化出现之前，我们不谈 UI 的单元测试，哪怕是对于 UI 页面的测试来说都是一件非常困难的事情。其实组件化并不全是为了复用，很多情况下也恰恰是为了分治，从而我们可以分组件对 UI 页面进行开发，然后分别对其进行单元测试。

### UI 组件树

![](/files/-M1VmJxdXZScMlz_66L8)

React 已经让 UI 测试变得容易很多，React 组件都可以被简化为这样一个表达式，即 UI = f(data)，这个纯函数返回的只是一个描述 UI 组件应该是什么样子的虚拟 DOM，本质上就是一个树形的数据结构。

![](/files/-M1jB7M1r9nvthlBNUcz)

给这个纯函数输入一些应用程序的状态，就会得到相应的 UI 描述的输出，这个过程不会去直接操作实际的 UI 元素，也不会产生所谓的副作用。

## 组件行为测试

![](/files/-M1VmJxf9hx0lUVO29qs)

Enzyme 让我们在针对某个上层组件进行测试时，可以不用渲染它的子组件，所以就不用再担心子组件的表现和行为，这样就可以只对特定组件的逻辑及其渲染输出进行测试了。

但是这并不能给你以信心，我们在测试的时候应该尽可能模拟真实用户的行为。否则就会出现以下的情况，每个部位的功能都能正常工作，但是却没有集成到一起。

![](/files/-M1VmJxpcPGgNP1pSzpg)

> [Write tests, not too many, mostly integration | Kent C. Dodds Blog](https://kentcdodds.com/post/write-integration-tests/)

### 测试组件的交互行为

```javascript
import { render } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('should only show button when click input', () => {
  const { queryByPlaceholderText, queryByTestId } = render(<AddComment />)

  userEvent.click(queryByPlaceholderText('请输入新留言…'))
  expect(queryByTestId('reply-button')).toHaveTextContent('回 复')

  userEvent.click(queryByTestId('cancel-button'))
  expect(queryByTestId('reply-button')).toBe(null)
})
```

### 测试组件单元的接口集成

```javascript
import { render } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('should call sortBy when select sort selections', () => {
  let sortHandler = jest.fn()
  const { queryByTestId, queryByText } = render(<CommentTitle comments={comments} onSort={sortHandler} />)

  userEvent.click(queryByTestId('sort-comment'))
  userEvent.click(queryByText('从旧到新'))

  expect(sortHandler).toBeCalledWith('oldest')
})
```

## 也请你思考一下

* Cypress 和 Testing Library 的测试侧重点是什么？
* 面向接口编程，如何由测试驱动出更好的组件 props 设计？
* 先写测试与后补测试有何不同？什么样的组件接口测起来很困难？
* 为什么在组件级别需要你更关注于组件行为而不是样式？


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jimmylv.gitbook.io/tdd-frontend/coding/00-project-commentbox/03-test-driven-component-interaction-and-props.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
