導讀:網易雲商在開發測試過程中,遇到了一系列的問題。為了解決這些問題,網易雲商開發了一套前端精準測試工具,該工具已在多個項目中落地,本文将基于雲商的落地實踐,分享該方案開發的思路與實際效果。
文 | 邱瑤瑤
網易雲商
背景
什麼是精準測試?精準測試是通過一系列技術手段,對測試過程産生的資料采集、存儲、計算、彙總,并進行可視化、分析、改進與優化。為什麼要做前端精準測試?在目前網易雲商的開發測試過程中,面臨以下問題:
- 系統複雜度高:随着業務發展,自身應用代碼複雜度會不斷增加,前端⼿⼯⿊盒測試覆寫度低,成本⾼,如何準确、全面判定代碼修改影響範圍會越來越重要。
- 需求疊代快:随着靈活開發在項目中的推進,測試時間也被縮短,測試範圍需要開發測試人員根據代碼和業務熟悉程度精确把控。
- 業務元件化:為了提升代碼複用性,開發同學一般會将代碼功能抽象成元件,經常發生因為修改一個元件或者公共方法沒有回歸到其修改影響而引起的 bug,但單元測試在業務代碼變更頻繁的情況下難以推行。是以需要通過前端精準方案及相應工具,準确判斷前端代碼影響的範圍,以及需要有可信的數字化名額來衡量前端測試的效果,做到前端測試精準化、可信化。
業内方案
目前,針對前端精準測試,業内許多公司都有所實踐,包括 VIVO 馬可平台、貓眼、Shopee、有贊等,這些方案大多聚焦于代碼覆寫率這個次元來度量測試的情況,缺乏對代碼影響範圍的評估。
優勢 | 劣勢 | 平台介紹 | |
VIVO馬可平台 |
| 缺少影響範圍評估 | Vivo馬可平台 https://www.cnblogs.com/vivotech/p/15783555.html |
貓眼 |
| 缺少影響範圍評估 | 貓眼前端代碼覆寫率工具 https://juejin.cn/post/6974294579981844516 |
Shopee |
| 缺少前端代碼的調用關系鍊 | 全棧代碼測試覆寫率及用例發現系統的建設和實踐 https://zhuanlan.zhihu.com/p/535884029 |
有贊 |
| 缺乏影響範圍的合理評估 | 前端精準測試探索:覆寫率實時統計工具 https://tech.youzan.com/front-coverage/ |
方案與架構
- 解決“我要測什麼”:我們通過代碼修改所影響檔案和影響路由,作為前端精準測試的範圍推薦,幫助開發、測試同學明确測試内容,解決“我要測什麼”的問題,實作測試精準化。
- 解決“我測的怎麼樣”:通過元件覆寫率、路由覆寫率、代碼覆寫率這三大次元作為前端測試覆寫程度分析的依據,解決“我測的怎麼樣”問題,實作測試可信化。
本方案的整體設計結構如下所示:
擷取代碼修改影響範圍
代碼檔案依賴關系擷取
要擷取代碼修改影響範圍,前提是擷取代碼中檔案依賴關系。檔案依賴擷取方案對比如下:
基于解析時長和靈活性的考慮,最終選擇自行開發,基于 babel 擷取源碼的 AST 資訊,擷取源碼的依賴關系。
代碼修改影響範圍方案主要分成三個部分:
- 第一部分:通過 simpleGit 擷取修改檔案的 diff 資料。
- 第二部分:檔案依賴解析子產品,根據配置的入口檔案,解析源碼的 AST,解析 import、export 等節點,擷取檔案依賴的關系。
- 第三部分:根據 diff 資訊和檔案依賴關系擷取代碼修改所影響的檔案及路由,并上報到伺服器。
擷取代碼修改所影響路由資料。對于測試同學來說,更關心的資料是代碼修改影響到了哪些路由,那麼如何得知?我們需要知道以下資訊:
1. 檔案的依賴關系:
{
file:"A.js",
depedFiles:["B.js","C.js"]
}
示例中表示 B.js 與 C.js 檔案中引入了 A.js 檔案。
檔案的依賴關系可以構成一個有向圖的結構,如下圖所示:
2. 路由及路由根元件所對應的檔案路徑資訊的映射關系:即某個路由下,對應的根元件的代碼路徑。
routes:[
{
"path": "/admin/a1",
"componentPath": "G.js"
},
{
"path": "/admin/b1",
"componentPath": "H.js"
}
]
通過判斷修改的檔案與路由根元件所對應的檔案之間是否存在路徑,即可判斷該檔案的修改是否影響到了此路由。
路由映射關系的擷取
路由映射關系如何擷取?最簡單的方式是在每個工程下面有一個配置檔案,檔案中配置每個路由對應的根元件的相對路徑,例如上述映射關系所示例。
此方案簡單且适配各類技術架構,不過存在以下問題:
- 需要手動收集目前的路由及元件配置情況。
- 以後寫新路由的時候,都需要額外再寫一份代碼。
能不能有更加自動化的擷取該映射關系配置的方法呢?
項目組的技術棧基本都是 react+react-router,在 Route(V4 及以上)等元件中,如下圖所示,props 裡中存在 path 及 component 資料,path 為配置的路由 url,component 對應着相關元件,即路由與元件的映射關系,是否可以通過什麼方式命中這個路由,擷取這兩個資訊,再生成整個項目的配置檔案?
而且,目前 component 對象下隻包含了元件函數的聲明,并沒有元件所在的代碼路徑,故我們需要額外在元件中注入代碼路徑這一資訊。
由此,設計了以下方案:
- 需要對每個 react 元件注入該元件所在的路徑 effectFilePosition 的屬性。
- 在命中對應的路由元件時,上傳該元件的路由資訊 path 和位置資訊 effectFilePosition 到本地的伺服器,本地伺服器會進行處理,在項目工程根目錄下生成一個 ppeffect.json 檔案,裡面儲存着路徑和位置的映射關系。
對元件注入所在路徑的屬性
開發了 webpack-component-position-plugin 的 webpack 插件,注入該元件所在的路徑 effectFilePosition 的屬性。
function addPath(parser, node) {
if (updatedNodes.has(node)) {
return
}
const componentName = node.id.name
const position = path.relative(process.cwd(),parser.state.current.resource)
const dep = new ModuleAppenderDependency(`;try{${componentName}.effectFilePosition="${position}";}catch(e){}`, node.range)
dep.loc = node.loc
parser.state.current.addDependency(dep)
updatedNodes.add(node)
}
效果展示:
為路由元件添加上報功能
開發 pp-effect-router 包,提供 withEffectSwitch、withEffectRouter 等高階方法,為 Route、Switch 等元件加上上報伺服器功能。
自動為項目中的 Route、Switch 等元件加上上報的功能
在項目中一個個手動修改 Route 和 Switch 等方法是不現實的,代碼侵入性也非常強,通過開發 babel 插件,自動為路由元件接入上報的功能。我們的訴求即需要把以下代碼:
import { Route as ReactRoute, Switch, Redirect } from 'react-router';
const Route = (props)=>{
//doSomething
return <ReactRoute {...props}/>
}
轉換成:
import { withEffectSwitch, withEffectRouter } from "@ysf/pp-effect-router";
import { Route as EffectRoute, Switch as EffectSwitch, Redirect } from 'react-router';
const Switch = withEffectSwitch(EffectSwitch);
const ReactRoute = withEffectRouter(EffectRoute);
通過開發 babel 插件 react-effect-import 實作以上功能,babel 插件配置友善,不需要該功能可不接入。
至此實作了隻要通路相對應的路由,即可自動生成路由和檔案位置資訊的映射關系。
影響範圍資料的上報
目前有三種情況會觸發代碼變更影響範圍的資料計算與上報,分别是:
- 本地送出代碼 commit 時:通過配置 pre-commit 鈎子觸發 pp-effect 相關指令,在送出 commit 時,計算本次 commit 的代碼變更影響,列印到終端中顯示,為開發同學提供參考。
- 觸發 GITLAB CI/CD 任務:通過配置 CI/CD,送出代碼後,執行 pp-effect 指令,将送出代碼所在分支與 master 分支進行對比,計算本分支的代碼變更影響,并推送給伺服器。
- 測試環境部署時觸發任務:通過配置測試服部署檔案,在部署測試環境時,執行 pp-effect 指令,将送出代碼所在分支與 master 分支進行對比,計算本分支的代碼變更影響,并推送給伺服器。
元件覆寫率和路由覆寫率上報
在雲商的前端精準測試方案中,我們開發了配套的 Chrome 插件,實作元件覆寫資料和路由覆寫資料的上報。
通過 Chrome 插件可快速檢視某個分支所修改的檔案、以及影響的路由等資訊;清單可展示兩種次元:分别是路由次元和檔案次元。
- 檔案次元下,可以檢視修改的檔案影響到了哪些路由。
- 路由次元下,可以看到該路由下都有哪些檔案被修改。
兩個次元切換友善測試同學把控測試。我們通過點選清單中的路由,即可跳轉到對應頁面,并觸發路由覆寫率的上報;而通過點選檔案,可觸發該檔案的覆寫率上報,由精準伺服器記錄與計算。
前端代碼覆寫率方案設計
前端代碼覆寫率整體方案
前端代碼覆寫率方案整體分成以下 4 個部分:
- 代碼插樁:通過使用 babel-plugin-istanbul 的 babel 插件,對代碼進行插樁。
- 覆寫率上報:通過 webpack-effect-report-plugin 插件,在代碼中注入工程名、分支、代碼 diff 資訊等,使用者通路頁面時,通過 pp-effect-report 定時向伺服器上報覆寫率資料。
- 覆寫率資料處理:node-effect 是一個 node 伺服器,提供了覆寫率上報接口、覆寫率資料展示,使用 istanbul-lib-coverage 實作覆寫率資料合并,istanbul-lib-report 和 istanbul-reports 等實作覆寫率資料的展示,将覆寫率資料存儲至精準平台伺服器中。
- 覆寫率資料展示:通過精準平台提供展示入口,調用 node 伺服器提供的接口,展示覆寫率資料;
代碼插樁
首先,要擷取到代碼覆寫率需要對代碼進行插樁。那麼,何為代碼插樁呢?代碼插樁指的是在保證被測程式原有邏輯完整性的基礎上在程式中插入一些探針(又稱為“探測儀”,本質上就是進行資訊采集的代碼段,可以是指派語句或采集覆寫資訊的函數調用)。插樁方法基本來自于兩個開源覆寫率統計架構,istanbul.js 以及 istanbul-middleware ,這兩個架構提供了若幹個插樁方法。
由于雲商項目都是使用了 babel,考慮選擇 babel-plugin-istanbul babel 插件,進行編譯插樁、改造成本低,由于僅在測試服使用,故包增大問題可以接受。
在工程中配置插件,在項目頁面中,會注入一個全局變量__coverage__,即是覆寫率資料
覆寫率資料上報思考
目前,主流有兩種形式的覆寫率上報方式,基于适用性和精确性等方面考慮,使用在項目中嵌入上報代碼。
通過 webpack-effect-report-plugin 插件,将工程資訊、分支資訊等注入到項目中。
此外,通過 pp-effect-report 插件,定時上報覆寫率資料;在上報覆寫率資料的時候,會依據 diff 的資訊,目前隻上報修改的檔案的覆寫率資料。
覆寫率處理伺服器
覆寫率處理器提供兩個接口:
- 上報接口:根據分支 branch、工程名 projectName 等,先去覆寫率資料庫中拉取覆寫率資料率,和上報的資料進行處理、合并,再存儲至覆寫率資料庫中。
- 展示接口:根據所要展示的分支 branch、工程名 projectName,拉取相應的資料,通過 istanbul-lib-report 和 istanbul-reports 的處理,傳回報告頁面資料。
不過 istanbuljs 的覆寫率報告缺乏對代碼 diff、增量代碼、增量代碼覆寫率相關的處理,從測試角度更在乎的是增量代碼覆寫率的次元;需要進行一定的二次開發。
這裡對 istanbul-reports 進行了二次開發,在上報資料時,标注了代碼增量的行數,判斷改增量行是否被覆寫,經過處理後,覆寫率資料 coverageMap 對象具有了兩個新的屬性,分别是 incrementStatementMap 和 incrementCoverdS,在後續 istanbul-reports 生成報告時通過解析這兩個新增的屬性,可以展示報告中行的新增情況和覆寫情況。
前面有➕加号代表為新增,加号但是有(x)代表新增未覆寫代碼;
小結
通過以上前端精準測試工具及平台,從代碼修改所影響的檔案範圍以及路由範圍實作了對前端測試資料範圍的推薦,通過對元件覆寫率、路由覆寫率以及代碼增量覆寫率實作前端測試覆寫程度分析的度量。目前該工具已在智企的多個項目中落地,代碼覆寫率方案仍處于初級階段有待完善。前端精準測試平台未來也會從資料推送精準性、可用性、自動性等多個方面進行優化,尋找更優實踐方案。