W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
測(cè)試是軟件開發(fā)中必不可少的一個(gè)環(huán)節(jié),程序化測(cè)試能夠加快研發(fā)速度,提高協(xié)作效率,減少產(chǎn)品故障。
通過本節(jié),你將學(xué)會(huì):
傳統(tǒng)前端項(xiàng)目的測(cè)試,可以從兩個(gè)維度去做分類:
從粗細(xì)粒度的角度看,測(cè)試分為以下幾類:
主要針對(duì)JS中的某些方法(不包括ux中的定義),這些方法獨(dú)立性強(qiáng),不強(qiáng)依賴于外部環(huán)境;
這類需求有明確的輸入輸出要求,通過常規(guī)的測(cè)試框架即可完成,如:mocha、Jest;
主要針對(duì)功能比較完整的模塊或者組件,它們是多個(gè)單元/模塊的組裝與業(yè)務(wù)結(jié)合;
這類需求希望面對(duì)常見的業(yè)務(wù)應(yīng)用場(chǎng)景,能夠得到正確的界面渲染與數(shù)據(jù)結(jié)構(gòu);
在當(dāng)前的快應(yīng)用平臺(tái)中,如果用到自定義組件或者底層接口(如:@system.fetch),需要依賴于真機(jī)環(huán)境進(jìn)行測(cè)試確認(rèn)結(jié)果;
當(dāng)然,部分開發(fā)者希望能夠提供一個(gè)模擬環(huán)境(如:NodeJS)的快應(yīng)用平臺(tái),方便在PC上完成測(cè)試,目前這個(gè)能力僅團(tuán)隊(duì)內(nèi)部使用,考慮到更新頻次較高,暫沒有對(duì)外開放;
實(shí)際上,優(yōu)先推薦開發(fā)者使用真機(jī)環(huán)境通過自動(dòng)化的方式完成確認(rèn),這樣可以確保多手機(jī)廠商設(shè)備下對(duì)功能的統(tǒng)一能力確認(rèn);
針對(duì)這類測(cè)試的實(shí)現(xiàn),開發(fā)者可以考慮在快應(yīng)用項(xiàng)目中建立新的能力測(cè)試頁(yè)面,引入待測(cè)試的自定義組件與模塊,通過?mocha
?、?Jest
?等工具完成斷言;
主要針對(duì)項(xiàng)目與頁(yè)面級(jí)別的測(cè)試,確保項(xiàng)目的基本功能暢通,是對(duì)項(xiàng)目上線的一個(gè)主要保障;
這類需求的原理就是通過真實(shí)的瀏覽器去運(yùn)行每個(gè)頁(yè)面,模擬用戶行為操作,確保界面的一致性,功能正確;
對(duì)于WEB的前端開發(fā)者通過?Karma
?、?Selenium
?等工具,完成對(duì)瀏覽器操作的自動(dòng)化封裝,最終測(cè)試頁(yè)面與后端服務(wù)器的正確配合;
對(duì)于快應(yīng)用的前端開發(fā)者,需要借助于一些接口與簡(jiǎn)單類庫(kù)封裝,來承載頁(yè)面的加載、切換等測(cè)試任務(wù);
從功能覆蓋的角度,測(cè)試可以分為幾類:
主要針對(duì)底層為前端開發(fā)者提供的接口,確保這些接口在跨設(shè)備上行為和輸出正確;如:?@system.storage
?;
主要針對(duì)頁(yè)面局部的UI布局渲染正確,確定:文本、對(duì)話框、滑動(dòng)等節(jié)點(diǎn)存在,位置正確;
主要針對(duì)某些行為操作下的功能表現(xiàn)正確,或者小到一個(gè)模塊、一個(gè)方法的輸出正確;
在快應(yīng)用的項(xiàng)目中,如果開發(fā)者僅僅只是JS文件中方法的單元測(cè)試的話(不需要引入底層接口),完全可以通過自己引入?mocha
?等工具,然后運(yùn)行在PC的NodeJS環(huán)境來實(shí)現(xiàn),這塊實(shí)現(xiàn)簡(jiǎn)單,本文不贅述;
當(dāng)前快應(yīng)用的實(shí)現(xiàn)中,框架為開發(fā)者提供了一套e2e的測(cè)試框架,這類測(cè)試需要運(yùn)行在真實(shí)的手機(jī)設(shè)備中,然后配合?@system.router
?接口完成頁(yè)面之間的切換與內(nèi)容測(cè)試,后面介紹原理;
開發(fā)者可以通過以下步驟來為項(xiàng)目引入e2e的測(cè)試能力(當(dāng)前使用的?hap-toolkit
?工具版本為:?0.6.8
?);
使用命令行新建一個(gè)自定義項(xiàng)目,名稱為:?quickapp-demo-quality
?
npx hap init quickapp-demo-quality
當(dāng)前,開發(fā)者也可以使用自己已有的項(xiàng)目,用于增加測(cè)試能力;
提示:為了方便開發(fā)者理解并使用,快應(yīng)用官方的 Github站點(diǎn) 提供了 示例項(xiàng)目 ;
在項(xiàng)目中,創(chuàng)建?test
?目錄,與?src
?目錄同級(jí),該文件夾用于存放所有的頁(yè)面測(cè)試用例;
其中針對(duì)每個(gè)頁(yè)面的測(cè)試用例的文件路徑需要與?src
?目錄中對(duì)應(yīng)頁(yè)面的路徑保持一致;
當(dāng)前項(xiàng)目我們添加?Demo
?、?DemoDetail
?、?About
?三個(gè)頁(yè)面的測(cè)試用例。結(jié)構(gòu)如下:
其中針對(duì)?Demo
?頁(yè)面的測(cè)試用例,舉例如下,其它測(cè)試用例的文件內(nèi)容類似:
/**
* @param vm 代表頁(yè)面的ViewModel實(shí)例
*/
export default function(vm) {
// 其中describe, it, expect函數(shù)來自于對(duì) mocha, chai的引入;
describe(`Demo`, function() {
it(`測(cè)試Detail頁(yè)面vm屬性`, function(done) {
expect(2).to.equal(3)
done()
})
it(`測(cè)試Detail頁(yè)面vm方法`, function(done) {
done()
})
})
}
在?test
?目錄下創(chuàng)建一個(gè)JS文件?autocase.js
?,表示所有要測(cè)試的頁(yè)面文件列表,其內(nèi)容如下:
代碼中描述了本次要測(cè)試的頁(yè)面為:?Demo
?、?DemoDetail
?、?About
?;
const autoCaseList = [
'Demo',
'DemoDetail',
'About'
]
export {
autoCaseList
}
該文件供下面的測(cè)試匯總頁(yè)面使用,聲明哪些頁(yè)面需要進(jìn)行?e2e
?測(cè)試。
上一步僅代表哪些頁(yè)面需要進(jìn)行測(cè)試,并測(cè)試頁(yè)面中的哪些能力;
這一步主要完成兩件事:
1) 增加測(cè)試匯總頁(yè)面,記錄測(cè)試結(jié)果;
2) 將各測(cè)試頁(yè)面的結(jié)果與切換連接起來,形成自動(dòng)化;
在?src
?目錄下創(chuàng)建一個(gè)測(cè)試匯總的頁(yè)面?Summary
?并在?manifest.json
?中聲明路由;
頁(yè)面內(nèi)容中的JS代碼部分舉例如下:
<script>
import router from '@system.router'
import {
autoCaseList
} from '../../test/autocase'
/**
* 獲取下一個(gè)自動(dòng)測(cè)試的page
*/
function findNextTestPage() {
const list = global.loadData('pageNameList')
const item = list.shift()
global.saveData('pageNameList', list)
return item
}
function waitForOK(time = 100) {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
export default {
private: {
// 包含自動(dòng)測(cè)試腳本的case列表
pageNameList: [],
pageTestList: [],
shouldTestAll: false,
showCompletedText: false,
isRunningTest: false
},
onInit() {
this.pageNameList = autoCaseList
// 初始化自動(dòng)化測(cè)試相關(guān)數(shù)據(jù)
if (global.loadData) {
global.saveData('pageNameList', this.pageNameList)
}
},
onShow() {
// 更新pageTestList
this.pageTestList = (global.loadData('pageTestList') || []).map(item => {
item.showPageTestDetail = false
item.tests.forEach(itemCase => {
itemCase.showPageTestErrDetail = false
})
return item
})
this.shouldTestAll && this.startNextTestPage()
},
/**
* 重啟整個(gè)所有測(cè)試
*/
restartTestProcess() {
// 防止連續(xù)多次點(diǎn)擊
if (!this.isRunningTest) {
this.isRunningTest = true
global.saveData('pageNameList', this.pageNameList)
global.saveData('pageTestList', [])
// 重置測(cè)試結(jié)束文本的顯示狀態(tài)
this.showCompletedText = false
// 自動(dòng)跑測(cè)試下一個(gè)測(cè)試用例
this.shouldTestAll = true
// 啟動(dòng)下個(gè)測(cè)試用例
this.startNextTestPage()
}
},
/**
* 啟動(dòng)下個(gè)測(cè)試用例
*/
async startNextTestPage() {
const pageItem = findNextTestPage()
console.info(`下個(gè)測(cè)試用例:${pageItem}`)
if (pageItem) {
await waitForOK(1000)
console.info(`開始測(cè)試頁(yè)面:${pageItem}`)
router.push({
uri: pageItem
})
} else {
this.isRunningTest = false
console.info(`測(cè)試用例列表執(zhí)行完畢`)
this.showCompletedText = true
this.shouldTestAll = false
}
},
gotoPage(path, params) {
// 單個(gè)頁(yè)面的點(diǎn)擊跳轉(zhuǎn):不會(huì)在測(cè)試后,自動(dòng)返回
params = Object.assign({
back: 'false'
}, params)
router.push({
uri: path,
params
})
},
togglePageTestDetailStatus($item) {
$item.showPageTestDetail = !$item.showPageTestDetail
},
togglePageErrStackStatus($item) {
$item.showPageTestErrDetail = !$item.showPageTestErrDetail
}
}
</script>
提示:開發(fā)者可以在 官方站點(diǎn)的示例項(xiàng)目 中查看 該頁(yè)面全部?jī)?nèi)容 ,路徑為:src/Summary/index.ux
在項(xiàng)目目錄下,執(zhí)行構(gòu)建命令 ?npm run build:test
?,并運(yùn)行在快應(yīng)用平臺(tái),即可完成自動(dòng)化測(cè)試;
掃碼打開頁(yè)面并點(diǎn)擊按鈕?點(diǎn)擊重新測(cè)試
?,完成一整套自動(dòng)化測(cè)試過程。
最終的示例效果如下:
上面這種e2e的測(cè)試方式,并不需要修改框架運(yùn)行時(shí),即:不需要前端框架配合修改某些代碼;
相反,它的實(shí)現(xiàn)主要是通過:上面的開發(fā)者代碼與?hap-toolkit
?編譯時(shí)工具在啟用參數(shù)?--enable-e2e
?構(gòu)建后注入的代碼配合完成的;
下面從編譯時(shí)、運(yùn)行時(shí)兩個(gè)方面,介紹實(shí)現(xiàn)原理,方便開發(fā)者理解,并進(jìn)行更深程度的定制與改造;
npm run build:test
?,將會(huì)啟用參數(shù)?--enable-e2e
?來創(chuàng)建RPK文件;開發(fā)者可以通過?package.json
?查看細(xì)節(jié);hap-toolkit
?將會(huì)完成以下幾件事,其中前兩步可以通過?build/app.js
?文件查看細(xì)節(jié),后兩部通過?build
?目錄下對(duì)應(yīng)的頁(yè)面JS查看細(xì)節(jié);app.ux
?中,注入測(cè)試相關(guān)類庫(kù):?hybrid-mocha
?、?hybrid-chai
?,他們分別是對(duì)類庫(kù)?mochajs
?、?chaijs
?的簡(jiǎn)單適配的封裝;app.ux
?中,注入一些全局函數(shù):?loadData(key)
?、?saveData(key, value)
?提供給每個(gè)頁(yè)面調(diào)用,這兩個(gè)函數(shù)分別用于向JS內(nèi)存中全局獲取數(shù)據(jù)與保存數(shù)據(jù);test
?目錄中對(duì)應(yīng)的測(cè)試用例文件(相對(duì)路徑保持一致的JS文件);mocha
?實(shí)例化與運(yùn)行的代碼,偽代碼如:?const mocha = new Mocha(); mocha.run();
?;hap-toolkit
?走正常流程,編譯每個(gè)頁(yè)面,如:?匯總頁(yè)面 Summary
?,并生成RPK文件;app.js
?,即:源碼中的?app.ux
?;mocha
?、?assert
?、?expect
?、?should
?測(cè)試類庫(kù),與全局函數(shù)?loadData(key)
?、?saveData(key, value)
?;manifest.json
?的定義,加載首頁(yè)?Summary
?,呈現(xiàn)匯總頁(yè)面的初始狀態(tài),此時(shí)還沒有執(zhí)行任何的頁(yè)面測(cè)試;mocha
?,并完成?test
?目錄下對(duì)應(yīng)的測(cè)試JS文件的執(zhí)行,并得到測(cè)試結(jié)果,最后返回到匯總頁(yè)面?Summary
?;Summary
?,此時(shí)會(huì)展現(xiàn)每個(gè)頁(yè)面的執(zhí)行結(jié)果;開發(fā)者可以點(diǎn)擊每條記錄,查看正確與出錯(cuò)的測(cè)試詳情;有些開發(fā)者,希望能夠?qū)鞈?yīng)用項(xiàng)目中的源碼在做測(cè)試的同時(shí),也能夠看到代碼執(zhí)行的覆蓋率,比如:使用 ?istanbul工具
?;
因此,介紹快應(yīng)用中使用?Istanbul
?的步驟:
--enable-istanbul
?(??hap-toolkit@0.6.13
?開始支持該參數(shù)
?);編譯時(shí)會(huì)對(duì)源碼進(jìn)行處理,增加Istanbul相關(guān)代碼監(jiān)測(cè)(行數(shù)、塊級(jí)、分支)的改造,最終生成編譯后的JS文件;
參考示例項(xiàng)目?quickapp-demo-quality
?中?package.json的build:test:istanbul
?命令;
__coverage__
?中;此時(shí),所有頁(yè)面的執(zhí)行結(jié)果都會(huì)保存下來,變量?__coverage__
?為普通的JS對(duì)象,每個(gè)屬性代表各頁(yè)面的路徑,對(duì)應(yīng)的值代表頁(yè)面運(yùn)行結(jié)果;
參考示例項(xiàng)目?quickapp-demo-quality
?中?build
?目錄下生成的各頁(yè)面JS代碼,搜索關(guān)鍵字?__coverage__
?;
此時(shí),開發(fā)者在頁(yè)面?src/Summary/Index.ux
?中點(diǎn)擊按鈕?保存代碼覆蓋率數(shù)據(jù)
?,事件通過?fetch
?接口,將記錄數(shù)據(jù)發(fā)送到PC上的快應(yīng)用NodeJS服務(wù)器,并以JSON格式保存數(shù)據(jù)在項(xiàng)目根目錄下的?.nyc_output
?文件夾中;
參考示例項(xiàng)目?quickapp-demo-quality
?中?src/Summary/index.ux
?頁(yè)面的?saveIstanbulCoverageData()
?方法;
此時(shí),開發(fā)者在根目錄下運(yùn)行:?nyc report
?,它會(huì)讀取根目錄下的配置文件?nyc.config.js
?完成轉(zhuǎn)換,并保存在文件夾?coverage
?中;
參考示例項(xiàng)目?quickapp-demo-quality
?中已經(jīng)安裝的依賴?類庫(kù)nyc
?,或者使用?npx nyc report
?也可以達(dá)到同樣效果;
coverage
?目錄中的?index.html
?頁(yè)面即可查看全部頁(yè)面的結(jié)果;下圖展示:記錄的頁(yè)面JS文件的表格數(shù)據(jù):
下圖展示:記錄某個(gè)頁(yè)面JS文件的詳細(xì)數(shù)據(jù):
當(dāng)前快應(yīng)用的測(cè)試方式與程序化能力,輔助開發(fā)者完成功能等上的保證,從而確定項(xiàng)目的穩(wěn)定性,提升維護(hù)性。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: