您现在的位置是:亿华云 > IT科技
前端测试一共有哪几种?
亿华云2025-10-03 20:06:39【IT科技】6人已围观
简介最近有不少朋友找到我聊了聊测试相关的内容,发现他们对测试的分类有些迷茫。实际上测试一共就 3 种:E2E,集成,单测,其它的功能测试、UI 测试、界面测试只是它们中里面的一种。K
最近有不少朋友找到我聊了聊测试相关的前端内容,发现他们对测试的测试分类有些迷茫。实际上测试一共就 3 种:E2E,前端集成,测试单测,前端其它的测试功能测试、UI 测试、前端界面测试只是测试它们中里面的一种。Kent C. Dodds[1]在这篇文章 《Static vs Unit vs Integration vs E2E Testing for Frontend Apps》[2]也聊到了这 3 种测试的前端对比和区别,除此之外,测试还聊到它们各自的前端适用场景,应该对还在迷茫中的测试同学有所帮助。所以今天把这篇文章分享给大家~
翻译中会尽量用更地道的前端语言,这也意味着会给原文加一层 Buf,测试想看原文的前端可点击 这里[3]。
J.B. Rainsberger 在我的采访里说了一个我很喜欢的比喻:
你可以把油漆扔到墙上,最终你可能会涂到大部分的墙壁,但除非你用刷子来刷墙,否则你永远不会刷到角落。
我喜欢用它来类比测试,因为做测试就跟刷墙一样,云服务器提供商在开始之前要选择正确的策略。你会用小刷头来刷墙么?当然不会。那会花很长时间,而且效果也不均匀。那你会用滚筒来刷所有东西么?比如拿它来刷两百年前你的曾曾祖母从别的地方带来的豪华家具?绝对不会。不同的刷子适用不同的场景,测试也是如此。
这就是为什么我会构建这个 测试模型[4]。
在这个模型里,有 4 种测试分类:
端对端测试:利用一个很像用户行为的机器人来和 App 交互,并验证功能是否正常。有时也会说 “功能测试” 或 E2E。集成测试:验证多个单元是否能协调共同工作。单元测试:验证单独隔离的部分是否正常工作。静态测试:捕获写代码时的错别字和类型错误。在这个模型里,每个测试分类的大小和你在测试时的关注度呈正相关(通常来说)。亿华云计算下面我来深入地聊聊这几种测试类型的区别、含义、以及如何对它们做优化。
测试类型
让我们从上往下看几个这类测试的例子:
端对端测试一般来说,它会跑完整个应用(前端 + 后端),这样的测试会像真实用户那样和应用进行交互。下面的例子是用 Cypresss 来实现的:
import { generate} from todo-test-utils
describe(todo app, () => {
it(should work for a typical user, () => {
const user = generate.user()
const todo = generate.todo()
// 这里我们会走通整个注册流程
// 我一般只会写一个测试来这么做
// 剩下的测试则会通过直接发 HTTP 请求来实现注册功能
// 这样我们就可以跳过这个注册表单的交互过程了
cy.visitApp()
cy.findByText(/register/i).click()
cy.findByLabelText(/username/i).type(user.username)
cy.findByLabelText(/password/i).type(user.password)
cy.findByText(/login/i).click()
cy.findByLabelText(/add todo/i)
.type(todo.description)
.type({ enter})
cy.findByTestId(todo-0).should(have.value, todo.description)
cy.findByLabelText(complete).click()
cy.findByTestId(todo-0).should(have.class, complete)
// 等等...
// 我的 E2E 测试一般都会写像真实用户那样
// 有时会写得非常长
})
})集成测试集成测试背后的思想就是尽可能少的 Mock。我一般只会 Mock 下面两点:
网络请求(用 MSW[5])实现动画的组件(因为谁会想在测试里等待呀)下面的测试用例会渲染整个应用。但这不是集成测试的硬性要求,而且大多数我写的集成测试都不会渲染整个 App。他们一般只会渲染 App 里要用到的 Provider(这就是 test/app-test-utils 伪模块中 render 要做的事):
import * as React from react
import { render, screen, waitForElementToBeRemoved} from test/app-test-utils
import userEvent from @testing-library/user-event
import { build, fake} from @jackfranklin/test-data-bot
import { rest} from msw
import { setupServer} from msw/node
import { handlers} from test/server-handlers
import App from ../app
const buildLoginForm = build({
fields: {
username: fake(f => f.internet.userName()),
password: fake(f => f.internet.password()),
},
})
// 集成测试一般只会用 MSW 这个库来 Mock HTTP 请求
const server = setupServer(...handlers)
beforeAll(() => server.listen())
afterAll(() => server.close())
afterEach(() => server.resetHandlers())
test(`logging in displays the users username`, async () => {
// 这个自定义的 render 会在 App 加载完成时返回一个 Promise
// (如果你用服务器渲染,你可能不需要这么做)
// 这个自定义的香港云服务器 render 还可以让你指定你的初始路由
await render(, { route: /login})
const { username, password} = buildLoginForm()
userEvent.type(screen.getByLabelText(/username/i), username)
userEvent.type(screen.getByLabelText(/password/i), password)
userEvent.click(screen.getByRole(button, { name: /submit/i}))
await waitForElementToBeRemoved(() => screen.getByLabelText(/loading/i))
// 检查用户是否已经登录了
expect(screen.getByText(username)).toBeInTheDocument()
})对这样的测试,一般我会做一些全局处理,比如 自动重置所有 Mock。
你可以在 React Testing Library setup docs 里了解更多关于上面的测试工具函数。
单元测试import @testing-library/jest-dom/extend-expect
import * as React from react
// 如果你的集成测试里有像上面一样的测试工具模块
// 那别用 @testing-library/react,直接用你的就好了
import { render, screen} from @testing-library/react
import ItemList from ../item-list
// 有些人可能不会把这样的测试叫做单测,因为我们还是要用 React 渲染成 DOM
// 他们还可能会告诉你要用 shallow render
// 当他们跟你说这个的时候,请把这个链接 https://kcd.im/shallow 甩他们脸上
test(renders "no items" when the item list is empty, () => {
render(
expect(screen.getByText(/no items/i)).toBeInTheDocument()
})
test(renders the items in a list, () => {
render(
// 注意:为了简化这个例子,这里用了 snapshot,不过仅限于:
// 1. snapshot 很小
// 2. 我们用了 toMatchInlineSnaphost
// 详情:Read more: https://kcd.im/snapshots
expect(screen.getByText(/apple/i)).toBeInTheDocument()
expect(screen.getByText(/orange/i)).toBeInTheDocument()
expect(screen.getByText(/pear/i)).toBeInTheDocument()
expect(screen.queryByText(/no items/i)).not.toBeInTheDocument()
})相信所有人都知道下面这个肯定是单测:
// 纯函数是单元测试的最佳选择,我还喜欢用 jest-in-case 来做单测
import cases from jest-in-case
import fizzbuzz from ../fizzbuzz
cases(
fizzbuzz,
({ input, output}) => expect(fizzbuzz(input)).toBe(output),
[
[1, 1],
[2, 2],
[3, Fizz],
[5, Buzz],
[9, Fizz],
[15, FizzBuzz],
[16, 16],
].map(([input, output]) => ({ title: `${ input} => ${ output}`, input, output})),
)静态测试
(译注:静态测试这里其实更多是指用 TypeScript 以及 ESLint 等静态检查工具来找出代码问题)
// 你能发现下面的问题么?
// 我相信 ESLint 的 for-direction 规则会比你 code review 时
// 更快发现这个问题很赞哦!(1714)
上一篇: 无线不设限丨新华三无线WA6500系列AP全面升级
下一篇: 数据中心运营面临热浪危机