日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

前端测试一共有哪几种?

發(fā)布時(shí)間:2023/12/31 HTML 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 前端测试一共有哪几种? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

哈嘍,大家好,我是海怪。

最近有不少朋友找到我聊了聊測(cè)試相關(guān)的內(nèi)容,發(fā)現(xiàn)他們對(duì)測(cè)試的分類(lèi)有些迷茫。實(shí)際上測(cè)試一共就 3 種:E2E,集成,單測(cè),其它的功能測(cè)試、UI 測(cè)試、界面測(cè)試只是它們中里面的一種。
Kent C. Dodds
在這篇文章 《Static vs Unit vs Integration vs E2E Testing for Frontend Apps》
也聊到了這 3 種測(cè)試的對(duì)比和區(qū)別,除此之外,還聊到它們各自的適用場(chǎng)景,應(yīng)該對(duì)還在迷茫中的同學(xué)有所幫助。所以今天把這篇文章分享給大家~

翻譯中會(huì)盡量用更地道的語(yǔ)言,這也意味著會(huì)給原文加一層 Buf,想看原文的可點(diǎn)擊 這里。


正片開(kāi)始

J.B. Rainsberger 在我的采訪里說(shuō)了一個(gè)我很喜歡的比喻:

你可以把油漆扔到墻上,最終你可能會(huì)涂到大部分的墻壁,但除非你用刷子來(lái)刷墻,否則你永遠(yuǎn)不會(huì)刷到角落。

我喜歡用它來(lái)類(lèi)比測(cè)試,因?yàn)樽鰷y(cè)試就跟刷墻一樣,在開(kāi)始之前要選擇正確的策略。你會(huì)用小刷頭來(lái)刷墻么?當(dāng)然不會(huì)。那會(huì)花很長(zhǎng)時(shí)間,而且效果也不均勻。
那你會(huì)用滾筒來(lái)刷所有東西么?比如拿它來(lái)刷兩百年前你的曾曾祖母從別的地方帶來(lái)的豪華家具?絕對(duì)不會(huì)。不同的刷子適用不同的場(chǎng)景,測(cè)試也是如此。

這就是為什么我會(huì)構(gòu)建這個(gè) 測(cè)試模型。

在這個(gè)模型里,有 4 種測(cè)試分類(lèi):

  • 端對(duì)端測(cè)試:利用一個(gè)很像用戶(hù)行為的機(jī)器人來(lái)和 App 交互,并驗(yàn)證功能是否正常。有時(shí)也會(huì)說(shuō) “功能測(cè)試” 或 E2E。
  • 集成測(cè)試:驗(yàn)證多個(gè)單元是否能協(xié)調(diào)共同工作。
  • 單元測(cè)試:驗(yàn)證單獨(dú)隔離的部分是否正常工作。
  • 靜態(tài)測(cè)試:捕獲寫(xiě)代碼時(shí)的錯(cuò)別字和類(lèi)型錯(cuò)誤

在這個(gè)模型里,每個(gè)測(cè)試分類(lèi)的大小和你在測(cè)試時(shí)的關(guān)注度呈正相關(guān)(通常來(lái)說(shuō))。下面我來(lái)深入地聊聊這幾種測(cè)試類(lèi)型的區(qū)別、含義、以及如何對(duì)它們做優(yōu)化。

測(cè)試類(lèi)型

讓我們從上往下看幾個(gè)這類(lèi)測(cè)試的例子:

端對(duì)端測(cè)試

一般來(lái)說(shuō),它會(huì)跑完整個(gè)應(yīng)用(前端 + 后端),這樣的測(cè)試會(huì)像真實(shí)用戶(hù)那樣和應(yīng)用進(jìn)行交互。下面的例子是用 Cypresss 來(lái)實(shí)現(xiàn)的:

import {generate} from 'todo-test-utils'describe('todo app', () => {it('should work for a typical user', () => {const user = generate.user()const todo = generate.todo()// 這里我們會(huì)走通整個(gè)注冊(cè)流程// 我一般只會(huì)寫(xiě)一個(gè)測(cè)試來(lái)這么做// 剩下的測(cè)試則會(huì)通過(guò)直接發(fā) HTTP 請(qǐng)求來(lái)實(shí)現(xiàn)注冊(cè)功能// 這樣我們就可以跳過(guò)這個(gè)注冊(cè)表單的交互過(guò)程了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 測(cè)試一般都會(huì)寫(xiě)像真實(shí)用戶(hù)那樣// 有時(shí)會(huì)寫(xiě)得非常長(zhǎng)}) })

集成測(cè)試

集成測(cè)試背后的思想就是盡可能少的 Mock。我一般只會(huì) Mock 下面兩點(diǎn):

  • 網(wǎng)絡(luò)請(qǐng)求(用 MSW)
  • 實(shí)現(xiàn)動(dòng)畫(huà)的組件(因?yàn)檎l(shuí)會(huì)想在測(cè)試?yán)锏却?#xff09;

下面的測(cè)試用例會(huì)渲染整個(gè)應(yīng)用。但這不是集成測(cè)試的硬性要求,而且大多數(shù)我寫(xiě)的集成測(cè)試都不會(huì)渲染整個(gè) App。他們一般只會(huì)渲染 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()),}, })// 集成測(cè)試一般只會(huì)用 MSW 這個(gè)庫(kù)來(lái) Mock HTTP 請(qǐng)求 const server = setupServer(...handlers)beforeAll(() => server.listen()) afterAll(() => server.close()) afterEach(() => server.resetHandlers())test(`logging in displays the user's username`, async () => {// 這個(gè)自定義的 render 會(huì)在 App 加載完成時(shí)返回一個(gè) Promise// (如果你用服務(wù)器渲染,你可能不需要這么做)// 這個(gè)自定義的 render 還可以讓你指定你的初始路由await render(<App />, {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))// 檢查用戶(hù)是否已經(jīng)登錄了expect(screen.getByText(username)).toBeInTheDocument() })

對(duì)這樣的測(cè)試,一般我會(huì)做一些全局處理,比如 自動(dòng)重置所有 Mock。

你可以在 React Testing Library setup docs 里了解更多關(guān)于上面的測(cè)試工具函數(shù)。

單元測(cè)試

import '@testing-library/jest-dom/extend-expect' import * as React from 'react' // 如果你的集成測(cè)試?yán)镉邢裆厦嬉粯拥臏y(cè)試工具模塊 // 那別用 @testing-library/react,直接用你的就好了 import {render, screen} from '@testing-library/react' import ItemList from '../item-list'// 有些人可能不會(huì)把這樣的測(cè)試叫做單測(cè),因?yàn)槲覀冞€是要用 React 渲染成 DOM // 他們還可能會(huì)告訴你要用 shallow render // 當(dāng)他們跟你說(shuō)這個(gè)的時(shí)候,請(qǐng)把這個(gè)鏈接 https://kcd.im/shallow 甩他們臉上 test('renders "no items" when the item list is empty', () => {render(<ItemList items={[]} />)expect(screen.getByText(/no items/i)).toBeInTheDocument() })test('renders the items in a list', () => {render(<ItemList items={['apple', 'orange', 'pear']} />)// 注意:為了簡(jiǎn)化這個(gè)例子,這里用了 snapshot,不過(guò)僅限于:// 1. snapshot 很小// 2. 我們用了 toMatchInlineSnaphost// 詳情:Read more: https://kcd.im/snapshotsexpect(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() })

相信所有人都知道下面這個(gè)肯定是單測(cè):

// 純函數(shù)是單元測(cè)試的最佳選擇,我還喜歡用 jest-in-case 來(lái)做單測(cè) 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})), )

靜態(tài)測(cè)試

(譯注:靜態(tài)測(cè)試這里其實(shí)更多是指用 TypeScript 以及 ESLint 等靜態(tài)檢查工具來(lái)找出代碼問(wèn)題)

// 你能發(fā)現(xiàn)下面的問(wèn)題么? // 我相信 ESLint 的 for-direction 規(guī)則會(huì)比你 code review 時(shí) // 更快發(fā)現(xiàn)這個(gè)問(wèn)題 😉 for (var i = 0; i < 10; i--) {console.log(i) }const two = '2' // 這個(gè)有點(diǎn)挑剔了,不過(guò) TypeScript 會(huì)告訴你這么做是不好的 const result = add(1, two)

測(cè)試的初衷

記住我們?yōu)槭裁磳?xiě)測(cè)試是很重要的。為什么你要寫(xiě)測(cè)試?是因?yàn)槲易屇銓?xiě)?是因?yàn)槿绻粚?xiě)測(cè)試你的 PR 無(wú)法通過(guò)?還是因?yàn)闇y(cè)試可以提升開(kāi)發(fā)體驗(yàn)?

我寫(xiě)測(cè)試最大、最重要的原因就是 CONFIDENCE(代碼信心)。我希望能夠信任未來(lái)我寫(xiě)的代碼不會(huì)在上線時(shí)弄崩整個(gè)應(yīng)用。所以,無(wú)論如何,
我都想確保這些類(lèi)型的測(cè)試都能給我來(lái)帶來(lái)最大的 CONFIDENCE,所以在做測(cè)試時(shí),要對(duì)它們做一些權(quán)衡。

如何取舍

我在這張圖里列了一些比較重要的點(diǎn):

上面的箭頭代表了你在寫(xiě)自動(dòng)化測(cè)試時(shí)要關(guān)注的 3 個(gè)取舍點(diǎn):

成本:¢ heap ? 💰🤑💰

當(dāng)你往模型的上面走時(shí),測(cè)試的成本會(huì)變得非常高。這不僅來(lái)自來(lái)真實(shí)在 CI 環(huán)境上跑所花的錢(qián),還來(lái)自開(kāi)發(fā)自己要編寫(xiě)和維護(hù)單個(gè)獨(dú)立測(cè)試所花的時(shí)間。

越往模型上方走,遇到的報(bào)錯(cuò)和失敗就越多,測(cè)試就越容易崩,從而導(dǎo)致需要更多時(shí)間來(lái)分析和修復(fù)測(cè)試。記住這句話,等會(huì)要考。

速度:🏎💨 ? 🐢

越往模型上方走,測(cè)試則跑得越慢。這是由于越跑高層級(jí)的測(cè)試,你就要跑更多的代碼。
而對(duì)單測(cè)來(lái)說(shuō),一般只測(cè)沒(méi)有依賴(lài)的小代碼片段,或者把依賴(lài)給 Mock 掉(會(huì)把上千行的代碼替換成簡(jiǎn)單幾行)。記住這句話,等會(huì)要考。

信心:簡(jiǎn)單問(wèn)題 👌 ? 大問(wèn)題 😖

一般人們?cè)诹臏y(cè)試金字塔模型 🔺 時(shí),都會(huì)聊到測(cè)試成本和速度的取舍。如果只考慮這兩點(diǎn),那對(duì)于這個(gè)金字塔模型,我肯定 100% 把精力放單測(cè)上,而
不去管其它的測(cè)試類(lèi)型。當(dāng)然我不能這么做,這是因?yàn)檫€有一個(gè)超級(jí)重要的因素,可能你經(jīng)常會(huì)聽(tīng)我說(shuō)到它:

當(dāng)你的測(cè)試和你應(yīng)用的使用方式越相似,它們能給你的信心就越大。

這是什么意思呢?意思是只有在用戶(hù)真實(shí)使用過(guò)后,才能保證你的應(yīng)用是正常工作的。但我們不可能真的去等一個(gè)真實(shí)的用戶(hù)來(lái)找 Bug 吧?這會(huì)要很長(zhǎng)時(shí)間,而且他可能會(huì)錯(cuò)過(guò)一些我們可能應(yīng)該測(cè)試的功能。再加上我們會(huì)定期發(fā)布軟件更新,任何人都無(wú)法用上最新的版本。

所以要怎么解決?我們要做權(quán)衡。 那我們應(yīng)該怎么做?我們可以寫(xiě)測(cè)試來(lái)測(cè)自己的應(yīng)用,而當(dāng)我們的測(cè)試不能像真實(shí)用戶(hù)那樣測(cè)試我們的應(yīng)用時(shí),我們就要對(duì)不同測(cè)試做權(quán)衡,只有這樣才能解決實(shí)際問(wèn)題。這就是這個(gè)測(cè)試模型中每一層我們要做的事。

當(dāng)你往測(cè)試模型的上方走時(shí),你也同時(shí)在提升我所說(shuō)的 “信心系數(shù)”。 這是你在那一層里能夠給你相對(duì)其它層的信心。你可以把模型最上層的測(cè)試想象成手動(dòng)測(cè)試,這肯定會(huì)給你非常強(qiáng)的信心,相對(duì)地,它們成本也很高,速度也很慢。

還記得剛剛就讓你記住兩件事么:

  • 越往上走,遇到的報(bào)錯(cuò)和失敗就越多,因此你的測(cè)試也越容易崩
  • 單測(cè)一般只用來(lái)測(cè)無(wú)依賴(lài)的小東西,或者把它的依賴(lài) Mock 掉再測(cè)試(把上千行代碼替換成幾行 Mock 實(shí)現(xiàn))

這兩點(diǎn)說(shuō)的都是:越往下走,你能測(cè)到的代碼就越少。所以,如果你在做低層級(jí)的測(cè)試,會(huì)需要更多測(cè)試用例來(lái)覆蓋應(yīng)用程序中相同數(shù)量的代碼。實(shí)際上,當(dāng)你越往模型下面走,會(huì)有很多東西是沒(méi)辦法測(cè)試的。

說(shuō)一下這些測(cè)試的問(wèn)題,靜態(tài)分析工具無(wú)法給你帶來(lái)任何對(duì)業(yè)務(wù)邏輯的信心。單測(cè)也無(wú)法確保你是否正確地使用依賴(lài)的(雖然你可以用斷言判斷它們是怎么被調(diào)用的,但是你仍然無(wú)法確保它在單測(cè)里是否被正確調(diào)用了)。UI 集成測(cè)試則是無(wú)法確保你是否正確把參數(shù)傳給后端,以及是否正確處理返回錯(cuò)誤。E2E 確實(shí)很好,但一般來(lái)說(shuō)你只會(huì)把它們放在測(cè)試環(huán)境下跑(類(lèi)生產(chǎn)環(huán)境,但是不是真生產(chǎn)環(huán)境)來(lái)獲取相對(duì)較高的代碼信心。

現(xiàn)在讓我們從另一個(gè)角度出發(fā):在模型的頂端,如果你想用 E2E 來(lái)檢查輸入文本和點(diǎn)擊提交后表單的邊界用例,你需要啟動(dòng)整個(gè)應(yīng)用來(lái)做很多初始準(zhǔn)備工作(后端也要),對(duì)這樣場(chǎng)景來(lái)說(shuō),用集成測(cè)試會(huì)更合適。而如果你想用集成測(cè)試來(lái)測(cè)試優(yōu)惠券的邊界情況,你可能要在初始函數(shù)里做一些準(zhǔn)備工作來(lái)渲染出優(yōu)惠券生成組件,然后才能通過(guò)單測(cè)更好地覆蓋邊界用例。而如果你想用單測(cè)來(lái)驗(yàn)證 add 函數(shù)沒(méi)有傳 number 而傳了 string 類(lèi)型的情況,使用像 TypeScript 這樣的靜態(tài)類(lèi)型檢查工具能更好地做驗(yàn)證。

總結(jié)

模型里每個(gè)級(jí)別都有自己的優(yōu)劣。一個(gè) E2E 測(cè)試會(huì)失敗很多次,所以很難追蹤哪些代碼導(dǎo)致的崩潰,但這也意味著它能給你帶來(lái)更多的信心。這樣的測(cè)試在你沒(méi)有時(shí)間寫(xiě)測(cè)試時(shí)是很有用的。我寧愿面對(duì)失敗多次的 E2E 測(cè)試,獲得更多代碼信心,也不想因?yàn)闆](méi)寫(xiě)而要處理更多的 Bug。

最后,我其實(shí)不在乎這些測(cè)試類(lèi)型之間的區(qū)別。 如果你說(shuō)我的單測(cè)就是集成測(cè)試,或者甚至說(shuō)是 E2E 測(cè)試(可能有人真的這么覺(jué)得 🤷?♂?),那就說(shuō)吧。而我更關(guān)心的是:它們能否給我足夠的信心去改以前的代碼和實(shí)現(xiàn)新業(yè)務(wù),所以我會(huì)結(jié)合不同的測(cè)試策略來(lái)達(dá)到這個(gè)目標(biāo)。


好了,這篇外文就給大家?guī)У竭@里了。文章主要講了 4 種測(cè)試類(lèi)型:靜態(tài)、單測(cè)、集成、E2E。其實(shí)在寫(xiě)測(cè)試的過(guò)程當(dāng)中,是很難區(qū)分你到底是在寫(xiě)哪種測(cè)試的,也不用一直想著:我在寫(xiě)哪類(lèi)的測(cè)試、項(xiàng)目測(cè)試種類(lèi)的比例怎么分、測(cè)試數(shù)量多少的問(wèn)題。就像 Kent 說(shuō)的那樣:我根本不在乎我寫(xiě)的是啥,我只在乎它是否能提高我的代碼信心就足夠了。

如果你喜歡我的分享,可以來(lái)一波一鍵三連,點(diǎn)贊、在看就是我最大的動(dòng)力,比心 ??

總結(jié)

以上是生活随笔為你收集整理的前端测试一共有哪几种?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。