# 任务 1：Cypress E2E 测试

Hey，你好，我是 JimmyLv 吕靖，这是你开始 前端 TDD 练习的第 11 天，今天我们会以 CommentBox 项目作为练习，学习如何使用 Cypress 进行 E2E 测试。

不知道你有没有这样的感觉，前端 **UI 级别的测试**会有这样一些痛点：

1. class name 老变来变去的，依赖 css selector 的测试都得改
2. UI styling 根本没法测，多 1 个 px 或少 1 个 px 也太过分了吧…
3. 很多跨浏览器的 hack，不同实现还得写测试样式什么的兼容性？
4. 响应式是个什么鬼？为什么天底下有这么多种不同分辨率的屏幕！？
5. ……

准备好代码库，让我们来一起尝试面对这些问题吧！

```bash
git clone https://github.com/JimmyLv/tdd-commentbox.git
cd tdd-commentbox

yarn install #安装依赖
yarn start #启动应用
yarn cy:open #运行E2E测试
```

当然，我还通过 CodeSandbox 的方式作为你的快速入口：<https://codesandbox.io/s/github/JimmyLv/tdd-commentbox> (可能需要科学上网)，帮助你去除设置开发环境的障碍，一键打开 Cloud IDE 即可开始编码，右边则是你的网站运行效果预览，比如：<https://tdd-commentbox.jimmylv.now.sh/>

{% embed url="<https://codesandbox.io/embed/github/JimmyLv/tdd-commentbox/tree/master/?autoresize=1&fontsize=14&hidenavigation=1&theme=dark>" %}
CodeSandbox 代码编辑
{% endembed %}

[![Edit tdd-commentbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/JimmyLv/tdd-commentbox/tree/master/?fontsize=14\&hidenavigation=1\&theme=dark)

## 练习目标

### 功能 1：基础评论框

一个最基本的评论框组件，带有作者头像、文本输入框，以及取消或评论操作按钮。

### 功能 2：留言列表

多个评论能够显示列表，并且包含列表数量和排序，并且每一条评论可以进行点赞或踩的操作。

## 本次任务的练习要求

使用 Cypress 测试驱动开发评论框组件的 功能 1 和 功能 2：

* 学习如何使用 Cypress 实施 TDD，可见即可得
* 使用 Cypress 测试不同分辨率下组件及页面的显示效果
* 注意，不要基于 CSS 属性作为选择器，阅读[Cypress 最佳实践](https://docs.cypress.io/zh-cn/guides/references/best-practices.html#%E5%85%83%E7%B4%A0%E9%80%89%E6%8B%A9)

## Cypress 使用案例

### E2E 用户行为测试

以添加 comments 为例，模拟真实用户行为。

```javascript
function addComment(commentText) {
  cy.queryByPlaceholderText('请输入新留言…').type(commentText)
  cy.queryByTestId('reply-comment').click()
}

describe('Comments Page', () => {
  beforeEach(() => {
    cy.visit('/')
  })
  it('should add new comment and show comment list', () => {
    const commentText = '唱跳Rap篮球！！！'

    cy.queryByTestId('comment-title').should('have.text', '留言板')
    cy.queryByText('留言为空').should('exist')

    addComment(commentText)

    cy.queryByTestId('comment-title').should('have.text', '1 留言')
    cy.queryByText('留言为空').should('not.exist')
    cy.queryByText(commentText).should('be.visible')
  })
})
```

### 响应式测试

添加 `cypress-image-snapshot` 测试，在页面回归时捕获不同分辨率下的视觉效果。

```javascript
# cypress/integration/visual-tests.spec.js
const sizes = [
  ['iphone-6', 'landscape'],
  'iphone-6',
  'ipad-2',
  ['ipad-2', 'landscape'],
  [1920, 1080],
]

const pages = ['/', '/blog/xxxx']

describe('Visual regression tests', () => {
  sizes.forEach(size => {
    pages.forEach(page => {
      it(`Should match previous screenshot '${page} Page' When '${size}' resolution`, () => {
        cy.setResolution(size)
        cy.visit(page)
        cy.matchImageSnapshot()
      })
    })
  })
})
```

### 兼容性测试

兼容性测试，也需要考虑。不同浏览器下的组件样式及行为，可能会有所不同。

![](/files/-M1VmJxYcvRtSx51R1bQ)

Cypress 4.0 已经支持跨浏览的 runner，参考 [Introducing Firefox and Edge Support in Cypress 4.0](https://www.cypress.io/blog/2020/02/06/introducing-firefox-and-edge-support-in-cypress-4-0/)

![](/files/-M1VmJx_CH_esUmnTsAr)

## 扩展阅读，思考一下？

在[为什么软件开发需要重构？ - 知乎](https://www.zhihu.com/question/19552812)这个问题下面，熊节提到：

> 今天的商业 IT 环境用一个词就可以概括：不确定。不确定带来颠簸和变动，不确定滋生怀疑和恐惧。

因为软件本身就无时无刻随着需求的快速变化而改变，那何来不改变的软件行为呢？「重构本质乃是不改变软件可观察行为与功能」这个前提是否依然成立呢？同理，运用在前端测试上来说，需求老变化，每次都得改测试可烦了，PM/UX 还没事儿跟你说这儿多个 px，那儿少个 icon 之类的…

可能有些同学就要得出结论：TDD 在后端好用在前端不好使。重构、测试驱动开发这些极限编程实践运用在后端业务需求，领域建模核心上来说很好使，这些东西也是从那时候开始发展而来的。但是现在这个商业时代各种客户端 UI 层出不穷，需求变动也比以往大得多，很多 APP 都是周更新，那这些方法论是不是也遇到了一些矛盾的地方？

![LEAN](/files/-M1jB7M-98i9jt5viAFq)

精益创业的方法论强调“开发（build）- 测量（measure）- 认知（learn）”的反馈循环，既然外部的商业环境变化莫测，那我们就不断得去试，好的 idea 继续加强，反之则尽快丢弃，不要一条路走到黑。最好的办法就是以最低的成本试，达成同样一个目标，尽可能少做事。

因此，让我们换个思路，从精益的角度来谈一谈 TDD，而测试只是 TDD 的附属品。需求变化，变化过快，并不会让测试带来的价值变少。单元测试的输入，本身就是基于我们对设计和实现方案的假设。需求变了，接口变了，测试跟着变是当然的，只不过是假设的输入变了。

比如说开发的时候，写好的单元测试，输入也是经常变，但原来的测试是不是就没有价值了？当然不是，测试挂掉的所有用例会帮我找出所有调用点。我不需要自己再去手动找，这个变的接口影响了哪些地方，我也不想找。把测试输入改成变后的需求，运行测试，挂掉的全部修好，然后再真实起应用跑，基本全是好的，就是不是，debug 起来也很轻松。

如果说抱怨测试经常要改输入改输出，那没有了测试接口需求变起来，验得岂不是更惨？说需求变了，因而测试价值减少了，因而原来就最好不写减少浪费，那变化之后的这些需求你又用什么来验证呢？还不是回到没测试裸奔手工验的状态。

React 所带来的好处，数据驱动和函数式思想(纯 I/O)，特别是有明显输入输出和数据强相关的地方，也让测试变得简单、单元模块化。衡量成本这件事情还是很难量化，感性得说，也许你不喜欢写 UI 测试，如果只写数据相关的测试会很爽，那么如果 UI=f(data)这个等式在 React 中被严格实施的话，把 data 控制好测好，那么最终的 UI 也一定是好的，不测也罢？

其实只是一个测试策略的问题。让我在后续的练习中揭晓答案，如何使用 TDD 思想实现组件驱动开发（[Component-Driven Development](https://blog.hichroma.com/component-driven-development-ce1109d56c8e)）？

**也请你思考一下：**

* 测试金字塔是什么？
* 为什么测试策略是金字塔结构？
* 测试金字塔每一层的侧重点是什么？


---

# 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/02-simplify-e2e-testing-by-cypress.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.
