国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

單測在商家前端業(yè)務中的實踐

開發(fā) 前端
目前現(xiàn)狀是前端大部分情況下都沒有接觸到單測,僅在組件庫或工具類的項目里有一些。這并不代表業(yè)務項目中前端就無法單測, 而是因為一些客觀原因,導致前端在單測上的投入相對較少。

1、背景

圖片

商家系統(tǒng)是提供給得物商家在得物平臺上可以穩(wěn)定運營的服務抓手,前端代碼也伴隨著系統(tǒng)的發(fā)展而不斷壯大。這樣將導致文檔卻更新不及時,最后想再通過這些文檔回溯業(yè)務邏輯也非常困難。

且若代碼結(jié)構(gòu)上沒有關(guān)注,動輒就會產(chǎn)出一個大幾千行的文件??,人員交替維護的時候很難理清里面的邏輯,維護非常困難。

2、前端單測的難點

為解決上述痛點,早在單測之前,團隊上已經(jīng)做了一些其他事情來使文檔更清晰、代碼質(zhì)量更高,如寫需求系分文檔、通過??整潔架構(gòu)(The clean architecture)??對代碼進行分層、code review等等。但這些其實都只是外在的約束,只有內(nèi)在的代碼能真正經(jīng)得住單測的推敲,才能更好的保障我們的代碼質(zhì)量。

但目前現(xiàn)狀是前端大部分情況下都沒有接觸到單測,僅在組件庫或工具類的項目里有一些。這并不代表業(yè)務項目中前端就無法單測, 而是因為一些客觀原因,導致前端在單測上的投入相對較少。

  1. 前端開發(fā)的內(nèi)容比較雜,一個需求不僅僅是功能函數(shù)的編寫,還有UI的展示、dom交互的綁定等等,且若想單測完全覆蓋,將包含非常多的內(nèi)容,對業(yè)務前端來說成本太高。
  2. 前端UI框架層出不窮,在業(yè)務開發(fā)的時候,依賴框架也很容易將代碼邏輯和UI等完全耦合在一起,導致一個文件上千行,很難對這種代碼找到單測的切入點。
  3. 單測上手本身就有一定的門檻,要寫出可維護性高的單測更不簡單,會讓不熟悉的人望而卻步。

3、單測即文檔

鑒于上面的第一個難點,前端涉及的內(nèi)容太雜,我們肯定無法給所有的代碼覆蓋單測,去測到代碼的各個角落。再結(jié)合上我們自己本身的痛點(文檔更新不及時,人員輪轉(zhuǎn)成本高),因此以“單測即文檔”為目標,我們只用覆蓋業(yè)務邏輯上的單測即可,只關(guān)注業(yè)務流程的銜接,通過用例將業(yè)務流程講清楚,對于單測的分支覆蓋率也不做強硬的要求。

Use Cases

因此,要在團隊落地單測的第一步即是識別出實現(xiàn)業(yè)務邏輯的代碼模塊。若在較早的時候,想找到這個切入點可能還真沒有什么好的方法,因為全是幾千行的大文件,且邏輯和UI都耦合在一起。

正如前面所說,在單測推行前,我們已經(jīng)做了一些代碼準備工作。得益于“整潔架構(gòu)”的推行,在開發(fā)需求的同時,已逐漸在對代碼進行解耦重構(gòu),其核心就是依據(jù)各部分代碼作用的不同將其拆分成不同的層次,在各層次間制定了明確的依賴原則,達到與框架無關(guān)與外部服務無關(guān)并可測試的目的。

圖片

經(jīng)過分層后,我們將業(yè)務邏輯主要都落在了usecase這一層,在我們的代碼結(jié)構(gòu)上,它的作用是將業(yè)務流程串聯(lián)起來,且它僅依賴entities(主要對服務端返回數(shù)據(jù)做適配和檢查)層,邏輯獨立不會因為依賴框架或UI的變化而無法運行。

相較于后端服務,前端應用通常并不會承載如計算、存儲等實實在在的業(yè)務邏輯,同時由于現(xiàn)在微服務架構(gòu)的流行,前端應用往往會承擔很重的膠水邏輯,即將各個微服務的邏輯串聯(lián)在一起,從而跑通業(yè)務流程。

因此,前端在編寫usecase的時候,我們會更注重主子函數(shù)的拆分,讓主usecase更純粹的去描述業(yè)務流程,而將部分具體的實現(xiàn)拆分到子函數(shù)中去實現(xiàn)。


/*
usecase聚焦流程的描述,諸如url鏈接拼接、活動期查詢等具體邏輯都拆分到了其他的模塊中
*/
async function exportActivityLog({count, formValues}: {count: number;formValues: LogData}) {
if (count > 5000) {
message.error('導出文件數(shù)量不得超過5000!')
return
}
const res = await checkIsDuringTheEventApi()
if (res.isDuring) {
message.error('活動期間,功能暫不可用,如有疑問聯(lián)系運營');
return
}
const url = generateDownloadUrl({ formValues })
downloadExcelFile(url)
}

function generateDownloadUrl() {
// 省略
}

因此,對usecase層寫單測,正是我們要找的最好切入點,其既能滿足我們將業(yè)務文檔進行補充,同時又能有單測模塊的產(chǎn)出,保障我們的代碼質(zhì)量和程序的穩(wěn)定性。

4、單測實踐

在識別出要覆蓋單測的代碼模塊之后,下一步自然就是落地單測用例。

前面已說過,寫單測本身就有一定的門檻,但既然要寫就應寫可維護性和穩(wěn)定性高的單測。否則代碼稍微一重構(gòu),單測崩了??;或代碼真崩了的時候,單測卻沒又通過了??。

根據(jù)前面的描述可以看出,我們對于用例的可讀性(文檔性)和穩(wěn)定性有極高的訴求,對于用例所測試的邏輯范圍要求不高,這個準則對于后續(xù)的單測用例的設(shè)計取舍會有很大的影響。

4.1 用例設(shè)計

首先我們需要確定設(shè)計用例的切入點,目前單測社區(qū)內(nèi)比較流行的模式無非TDD和BDD兩種:

TDD:測試驅(qū)動開發(fā),偏向于去測到函數(shù)的各個功能運行的結(jié)果是否符合預期,由于是通過先寫用例去驅(qū)動業(yè)務邏輯的實現(xiàn),因此用例的設(shè)計往往更偏技術(shù)實現(xiàn)。

BDD:行為驅(qū)動開發(fā),流程上是TDD模式的一種分支,區(qū)別在于在構(gòu)思用例的時候更多的是以用戶行為(user story)的角度去考慮。

圖片

關(guān)于兩者更多的區(qū)別,大家可以網(wǎng)上查閱到更多的資料,這里就不再贅述。為了我們單測的穩(wěn)定可維護性,且以文檔為導向的我們,自然是選用了BDD的模式,只測業(yè)務行為邏輯,不關(guān)注功能函數(shù)的輸出正確與否(這塊目前可在自測和測試兄弟團隊那邊幫忙保障)。這樣除非業(yè)務流程發(fā)生變更,否則代碼一般的重構(gòu)或調(diào)整都不會影響到單測的運行,不會造成單測的雪崩。

4.2 用例結(jié)構(gòu)

在用例結(jié)構(gòu)上,為了配合“單測即文檔”的初衷并更好的配合BDD,我們在社區(qū)常見的AAA(Arrange-Act-Assert)和GWT(Given-When-Then)兩種結(jié)構(gòu)之間選擇了后者。

無論AAA還是GWT最終都會形成一個三段式的用例結(jié)構(gòu),其區(qū)別仍然在于AAA的構(gòu)思更傾向于技術(shù)實現(xiàn),GWT更傾向于業(yè)務流程。雖然結(jié)構(gòu)一樣,但設(shè)計出來的用例內(nèi)容會有很大區(qū)別。

Given-When-Then

Given:一個上下文,指定和準備測試的預設(shè)

When:進行一系列操作,即所要執(zhí)行的操作

Then:得到可觀察的結(jié)果,即需要檢測的斷言

我們根據(jù)GWT的提供了單測的基本模板,供組內(nèi)同學寫單測時直接使用。


function init() {
const checkIsDuringTheEventApi = jest.fn();
const downloadExcelFile = jest.fn();
const exportActivityLog = buildMakeExportActivityLog({checkIsDuringTheEventApi, downloadExcelFile})

return {
checkIsDuringTheEventApi,
downloadExcelFile,
exportActivityLog
}
}

describe('spec', () => {
it('test', () => {
// Given 準備用例所需的上下文
const { checkIsDuringTheEventApi, downloadExcelFile, exportActivityLog } = init();

// When 調(diào)用待測的函數(shù)
exportActivityLog()

// Then 斷言
expect('expect')
})
})

對于一些校驗簡單模型的用例,通過init函數(shù)做一層封裝就夠用了。但對于業(yè)務邏輯比較復雜,字段比較多的模型,直接利用原生數(shù)據(jù)進行初始化對用例的可讀性并不友好。


describe('spec', () => {
it('個人賣家未發(fā)貨的訂單,允許進行取消操作', () => {
// Bad case: 依賴字段較多,這樣手動去創(chuàng)造字段數(shù)據(jù)可讀性并不友好
// 若case較多,這些字段要手動構(gòu)建多次
action({
status: Status.待發(fā)貨,
merchantType: MerchantType.個人賣家,
// ...還有一些其他必傳字段
})
})
}

對于這種復雜場景,我們傾向于使用builder模式來構(gòu)造數(shù)據(jù),在較小的開發(fā)成本下保障用例的可讀性和可維護性。

describe('spec', () => {
it('個人賣家未發(fā)貨的訂單,允許進行取消操作', () => {
// Good case:通過builder實現(xiàn)邏輯的復用和信息的聚焦
const order = new OrderBuilder()
.status("待發(fā)貨")
.merchantType("個人賣家")
.build()

action(order)

})
})

4.3 用例描述

既然是要作為文檔使用,那用例描述上也顯得至關(guān)重要了。相比TDD對功能函數(shù)的單測,我們描述完全于GWT的用例結(jié)構(gòu)對應(When時常會被省略掉),我們并不關(guān)心具體的技術(shù)實現(xiàn)細節(jié),更多的是描述的這個業(yè)務的行為流程,思考函數(shù)最終想做什么,達到什么目的。基于意圖,把被測函數(shù)當做黑盒,不用關(guān)注其中間的實現(xiàn)細節(jié),究竟生成了什么臨時變量、循環(huán)了幾次、有什么判斷等,而是通過用例描述將業(yè)務流程講清楚。


describe('導出活動日志', () => {
it('導出時,先查詢當前活動狀態(tài),若狀態(tài)是未在進行中,則執(zhí)行導出操作', () => {
// 省略...
})
it('導出時,若導出數(shù)量大于5000條,將不允許導出', () => {
// 省略...
})
})

上面??是導出活動日志的一個操作,可以看出,用例的描述不會像測功能函數(shù)那樣精簡(入?yún)⑹莂,調(diào)用了啥函數(shù)必須返回b之類),但是將導出活動時,相應的調(diào)用流程和條件描述了出來,這樣其他人在接手這塊業(yè)務時,通過這個用例就能清楚知道在導出活動日志時需求上有些什么限制以及要做的操作。

4.4 用例斷言

在確定好用例的設(shè)計思路和結(jié)構(gòu)之后,我們在用例的校驗內(nèi)容上也做了一些取舍。針對社區(qū)上主導的經(jīng)典測試(Classical)和模擬測試(Mockist)兩大陣營,結(jié)合“單測即文檔“的理念,我們對于業(yè)務流程的驗證訴求非常強烈,因此選擇了后者。

Classical風格是盡可能的使用真實對象和函數(shù),讓函數(shù)以及依賴都真實的執(zhí)行;相對的,Mockist是想盡辦法去mock,主張將所調(diào)用的被測函數(shù)全部mock。存在即合理,兩個派各有利弊,并不存在一定誰好誰差。

要對用到的函數(shù)進行mock,在保證用例可維護性的前提下(比如不mock文件路徑),我們需要對函數(shù)的依賴關(guān)系進行整理。得益于團隊整潔架構(gòu)的落地,目前應用的usecase層都已經(jīng)通過依賴倒置對依賴關(guān)系做了很好的管理(usecase只依賴entity)。


export default function buildMakeExportActivityLog({checkIsDuringTheEventApi,downloadExcelFile}) {
async function exportActivityLog({count,formValues}) {
if (count > 5000) {
message.error('導出文件數(shù)量不得超過5000!')
return
}
const res = await checkIsDuringTheEventApi()
if (res.isDuring) {
message.error('活動期間,功能暫不可用,如有疑問聯(lián)系運營');
return
}
const url = generateDownloadUrl({ formValues })
downloadExcelFile(url)
}
}

// index.ts
import {checkIsDuringTheEventApi} from '@/services/activity'
import {downloadExcelFile} from '@/utils'
import buildMakeExportActivityLog from './makeExportActivityLog'

export const exportActivityLog = buildMakeExportActivityLog({cancel,printSaleTicket})

可以看到checkIsDuringTheEventApi以及downloadExcelFile這兩個函數(shù)最終作為參數(shù)傳入到實際的函數(shù)中,他們一個將會去發(fā)起請求,一個是會調(diào)用window的方法進行下載,通過依賴倒置就能方便我們對其進行模擬,在單測時就不會去真實執(zhí)行這兩個函數(shù)。


function init() {
const checkIsDuringTheEventApi = jest.fn();
const downloadExcelFile = jest.fn();
const exportActivityLog = buildMakeExportActivityLog({checkIsDuringTheEventApi, downloadExcelFile})
return {
checkIsDuringTheEventApi,
downloadExcelFile,
exportActivityLog
}
}

usecase中時常會有依賴的函數(shù)要去發(fā)起請求,在單測時我們不會去真實去發(fā)起這個請求,因此對于這類函數(shù),我們都應mock掉,這樣可保障我們用例的速度和穩(wěn)定性。當然實際在寫單測中,我們也不應該成為一個完全的mockist,無休止的進行mock,更好的方式是兩者結(jié)合,否則濫用mock反而會導致單測寫起來會更繁瑣(因為要去mock所有調(diào)用的函數(shù)實現(xiàn)或場景),而且真實代碼寫起來也會很別扭(所有外部函數(shù)都依賴倒置)。

一個用例正確與否,最終依賴的是最后的斷言,那對我們來說該怎樣進行斷言呢,如前面一直強調(diào)的一樣,我們測的是邏輯行為,因此需斷言的是某個行為的是否執(zhí)行或者是否達到了什么目的。結(jié)合前面的mock,我們可對函數(shù)的調(diào)用情況進行捕獲,針對上面發(fā)起取消退款的函數(shù),斷言的例子如下:


describe('導出活動日志', () => {
it('導出時,先查詢當前活動狀態(tài),若狀態(tài)是未在進行中,則執(zhí)行導出操作', () => {
// 省略...
expect(downloadExcelFile).toBeCalled()
})

it('導出時,若導出數(shù)量大于5000條,將不允許導出', () => {
// 省略...
expect(downloadExcelFile).not.toBeCalled();
})
})

如上,斷言的內(nèi)容不是函數(shù)的實現(xiàn)細節(jié),如參數(shù)是否正確,而是只斷言行為是否執(zhí)行,它能盡量保證做到若代碼重構(gòu)后,單測用例在不修改的情況下依然能健壯的運行,其只依賴需求的變更而做更改。同時為了維護用例的穩(wěn)定性,單個用例我們通常僅執(zhí)行一次斷言(單一職責),斷言的內(nèi)容嚴格和描述的“Then”部分對應。

5、結(jié)語

商家以“單測即文檔”的理念為落地方向,在代碼設(shè)計以及用例的構(gòu)思、結(jié)構(gòu)、斷言、描述等環(huán)節(jié)都做了一定取舍,最終在用例的書寫成本、穩(wěn)定性、可讀性等各個方面取得了相對較好的平衡。

目前組內(nèi)各個項目已逐漸沉淀了幾百個用例,團隊內(nèi)相互支援或自己回顧時,通過這些用例就能知道這塊邏輯在做什么事,在修改這些需求時通過測試用例也能盡快知道基本的業(yè)務邏輯,有了單測的保障,改起代碼來更有底氣,代碼結(jié)構(gòu)上,也更加的合理。在大家逐漸熟悉單測后,后續(xù)更會慢慢做到功能函數(shù)、UI等的單測覆蓋,大家一起來保障商家前端業(yè)務的穩(wěn)定發(fā)展。

參考文章:

“整潔架構(gòu)”和商家前端的重構(gòu)之路:

??https://mp.weixin.qq.com/s/Sgr6El88eqjCDaRFxIVFQA??

The Difference Between TDD and BDD:

??https://joshldavis.com/2013/05/27/difference-between-tdd-and-bdd/??

??https://lassala.net/2017/07/20/test-style-aaa-or-gwt/??

jest文檔:

??https://jestjs.io/zh-Hans/docs/getting-started???

責任編輯:龐桂玉 來源: 得物技術(shù)
相關(guān)推薦

2022-07-06 08:34:17

前端單測項目

2022-04-10 11:52:43

前端單測程序

2022-03-29 09:03:22

測試組件Propsrender

2023-03-14 22:32:24

業(yè)務單測數(shù)量

2022-03-22 15:16:50

路由前端路由動畫

2022-05-26 21:33:09

業(yè)務前端測試

2016-10-28 15:01:35

Cookie前端實踐

2022-05-26 10:12:21

前端優(yōu)化測試

2016-01-12 11:38:19

智能化運維運維業(yè)務

2022-06-03 09:30:31

店鋪W3C體系渲染

2022-08-08 13:24:28

整潔架構(gòu)架構(gòu)前端

2025-03-20 10:50:08

RedisCaffeine緩存監(jiān)控

2024-07-11 07:02:01

2020-07-08 10:01:07

SDP網(wǎng)絡安全安全框架

2025-12-12 04:00:00

2017-06-19 16:45:41

數(shù)據(jù)庫水平切分用戶中心

2023-11-27 18:38:57

得物商家測試

2023-04-21 18:36:16

商家開發(fā)平臺

2022-01-06 09:55:19

鴻蒙HarmonyOS應用

2023-04-07 18:35:23

StarRocks貨品運營
點贊
收藏

51CTO技術(shù)棧公眾號

91精品国产综合久久久蜜臀粉嫩| 日本久久一区二区三区| 日韩激情av| 欧美三级黄网| 欧美日韩专区在线| 天堂va蜜桃一区二区三区漫画版| 欧美成人sm免费视频| 日韩精品毛片| 日本韩国欧美在线| 亚洲色图图片网| 亚洲另类色综合网站| 精品国产伦一区二区三区观看方式| 宅男深夜免费观看视频| ...av二区三区久久精品| 国产性生交xxxxx免费| 91麻豆精品在线观看| a√天堂在线观看| 91欧美激情一区二区三区成人| 老太脱裤子让老头玩xxxxx| 国产91精品露脸国语对白| 992tv快乐视频| 99免费精品在线观看| 日韩av资源在线| 日本一区二区久久| 日本福利片免费看| 欧美日韩在线免费| 色的视频在线免费看| 欧美一区二区三区啪啪| 精品精品导航| 日韩激情视频在线| 日韩美香港a一级毛片| 欧美黑人xxxⅹ高潮交| 日韩高清影视在线观看| 国产精品香蕉国产| 中文一区在线| 免费观看黄色的网站| 国产成人av影院| 分分操这里只有精品| 国产日产欧美精品一区二区三区| 九九99九九精彩| 亚洲第一搞黄网站| 国产小视频免费在线观看| 日韩精品最新网址| 日本免费在线一区| 国产91色在线|免| 黄色精品一区| a级网站在线观看| 国产亚洲精品bt天堂精选| 中文在线а√天堂官网| 91精品国产91热久久久做人人| 伊人久久视频| 97久久精品人搡人人玩| 黄色日韩精品| 黄色一级在线视频| 欧美性猛交xxxx偷拍洗澡| yellow在线观看网址| 萌白酱国产一区二区| 一区二区三区在线观看免费| 亚洲一卡二卡三卡| 亚洲人午夜精品天堂一二香蕉| 国产资源在线看| 亚洲老司机av| 国产亚洲第一伦理第一区| 久久久久久久久久久久久久久久av| 国产aⅴ综合色| 偷偷要 色偷偷| 欧美α欧美αv大片| 大伊香蕉精品在线品播放| 黄色一区三区| 国产欧美一区二区精品久导航| www黄在线观看| 精品国偷自产在线视频99| 91tv官网精品成人亚洲| 欧日韩免费视频| 在线观看一区日韩| 国产一区二区三区精品在线观看 | a级黄色小视频| 亚洲高清一区二区三区| 九色porny丨首页入口在线| 日本免费一区二区三区视频观看| 视频一区视频二区在线观看| 日本亚洲天堂| 上原亚衣av一区二区三区| 欧美精品激情| 日本成人黄色网| 精品少妇一区二区三区免费观看 | 一道本无吗dⅴd在线播放一区 | 欧美有码在线观看视频| 秋霞av亚洲一区二区三| 日本免费一二区| 在线中文字幕日韩| 羞羞视频在线观看欧美| 天天槽夜夜槽| 久久夜色精品国产欧美乱| 日韩精品一级中文字幕精品视频免费观看| 日本不卡1区2区3区| 久久久精品一区二区| 麻豆精品一区二区av白丝在线| 性感美女激情视频在线观看| 久久久国产一区二区| 精品一区二区三区在线观看国产| 中文在线三区| 2025国产精品视频| 久久综合九色综合97_久久久| 超碰资源在线| 欧美污视频久久久| 欧美视频日韩视频| 天天av综合| 欧美黑人巨大| 国产精品吹潮在线观看| 亚洲人成精品久久久久| 国产精品极品| 欧美黄色一级片视频| 日韩中文字幕在线免费观看| 国产成人av福利| 日韩精品99| 国产91沈先生在线播放| 日韩毛片中文字幕| 六月丁香婷婷色狠狠久久| 中文在线字幕免费观看| 美女被啪啪一区二区| 欧美精品亚洲一区二区在线播放| 欧美成熟视频| 99青草视频在线播放视| 懂色中文一区二区三区在线视频 | 亚洲精品电影网在线观看| 免费久久99精品国产自在现线| 国产有码在线| 亚洲一区二区三区成人在线视频精品 | 国产高清一级毛片在线不卡| 国产美女久久精品| 一区二区三区.www| 精品国产一区二区三区久久久樱花 | 亚洲精品视频在线播放| 精品在线免费观看| 午夜不卡影院| 成年人午夜免费视频| 久久天天躁夜夜躁狠狠躁2022| 99re热这里只有精品免费视频| 欧美亚洲二区| 亚洲国产精品三区| 国产精品久久不能| 日韩欧美在线免费| 亚洲免费网址| 中文字幕21页在线看| 色噜噜狠狠成人网p站| 亚洲xxx拳头交| 尤物视频在线免费观看| 日韩精品久久久| 精品女同一区二区| 国产一区二区三区在线观看免费视频| 欧美人体一区二区三区| 黄色www网站| 韩国日本不卡在线| 亚洲午夜激情av| 国内精品美女在线观看| 午夜伦理在线视频| 国产va亚洲va在线va| 97视频色精品| 在线国产电影不卡| 麻豆一区二区三| 91嫩草精品| 成人在线免费公开观看视频| 一区二区精品国产| 不卡av在线网站| 亚洲成av人片在线| 日韩精品色哟哟| 国产精品麻豆| 青春有你2免费观看完整版在线播放高清 | 超碰在线99| 嫩草影院国产精品| 岛国一区二区三区高清视频| 亚洲精品一区在线观看香蕉| 椎名由奈av一区二区三区| 亚洲另类黄色| 色综合一区二区日本韩国亚洲| 成年网站免费| 色一情一乱一伦一区二区三区 | 久久99国产精品99久久| 中文日韩电影网站| 亚洲一区中文在线| 日韩国产精品久久| 国产精品15p| 在线观看的网站你懂的| 欧美wwwwwww| 日韩欧美亚洲日产国产| 欧美性一区二区三区| 亚洲成色777777女色窝| 亚洲视频在线观看三级| 美腿丝袜亚洲综合| 欧美精品第一区| 无码小电影在线观看网站免费| 免费高清视频日韩| 国产在线无码精品| 亚洲iv一区二区三区| 美日韩在线视频| 亚洲精品理论电影| 91精品福利视频| 亚洲视频一二三| av一区二区三区在线|