Cypress和基于模型的測試實(shí)戰(zhàn)

本文來自七牛云投稿,作者:伏曉,聲網(wǎng)Agora Web引擎研發(fā)工程師。本文先簡單介紹了Cypress測試平臺(tái),之后介紹基于模型的測試(Model-Based Testing)方法,并使用其對流行的TodoMVC應(yīng)用進(jìn)行建模和測試。最后指出這一測試Demo的可改進(jìn)之處,同時(shí)展望了基于模型測試的未來發(fā)展。
Cypress介紹
Cypress是為現(xiàn)代Web App打造的下一代前端測試工具。使用現(xiàn)代JavaScript框架構(gòu)建Web應(yīng)用程序的開發(fā)人員或QA工程師都可以采用Cypress快速編寫和運(yùn)行E2E(端到端)測試、集成測試和單元測試。
Cypress 的 Time travel 功能是它的最大亮點(diǎn),支持回退至任意時(shí)間的 Snapshot, 像是在回放電影一樣,將測試運(yùn)行過程中的每個(gè)細(xì)節(jié)重現(xiàn)出來 。可以非??焖俚亩ㄎ粏栴},極大的提高了調(diào)試自動(dòng)化測試的體驗(yàn)。讓它與眾不同的是它的運(yùn)行機(jī)制:大多數(shù)測試工具(如Selenium)通過在瀏覽器外部運(yùn)行并在網(wǎng)絡(luò)上執(zhí)行遠(yuǎn)程命令來運(yùn)行,Cypress恰恰相反——Cypress在與你的應(yīng)用程序相同的生命周期里執(zhí)行。其他方面優(yōu)勢可以在https://docs.cypress.io/zh-cn/guides/overview/key-differences.html 看到。
在它的運(yùn)行界面中可以看到每一步的操作,只需點(diǎn)擊你想重現(xiàn)的步驟即可看到該步的截屏。如下圖所示,點(diǎn)擊“找到包含 type 的元素”這一行代碼,右側(cè)就會(huì)呈現(xiàn)出此時(shí)的場景,并高亮出這個(gè)元素。

具體的介紹可以到Cypress.io官網(wǎng)查看。也可以直接下載Cypress Real World App進(jìn)行真實(shí)應(yīng)用場景的測試體驗(yàn)。在這一展示場景中,運(yùn)行100個(gè)測試(包括約30個(gè)API測試和70個(gè)端到端測試)需要5分30秒,這一速度已經(jīng)比很多手工冒煙測試還要快。而他是完全自動(dòng)并可調(diào)式的。
需要注意的是必須等待Node.js服務(wù)器和React開發(fā)服務(wù)器啟動(dòng)完畢后才能運(yùn)行Cypress進(jìn)行測試。
基于模型的測試
基于模型的測試(Model-based Testing)屬于軟件測試領(lǐng)域的一種測試方法。按照此方法,測試用例可以完全或部分的利用模型自動(dòng)產(chǎn)生。MBT 則允許軟件開發(fā)人員和測試人員,只關(guān)注建立系統(tǒng)的正確性以及模型的規(guī)范性,再通過專門的 MBT 工具根據(jù)不同的測試用例設(shè)計(jì)策略從系統(tǒng)模型生成可靠的測試用例。測試Case是由模型生成,而不是由源代碼生成。因此,基于模型的測試又常常被當(dāng)作黑盒測試的一種形式。對于復(fù)雜的軟件系統(tǒng)中,如何應(yīng)用基于模型的測試還在探索中,在測試學(xué)術(shù)界已經(jīng)有一些關(guān)于自動(dòng)生成測試模型的論文發(fā)表。
優(yōu)點(diǎn)
1、測試用例的維護(hù)更輕松:維護(hù)好模型,無需關(guān)注測試用例的細(xì)節(jié)?
2、軟件缺陷發(fā)現(xiàn)的更早:在構(gòu)建被測系統(tǒng)模型的過程中,需要對被測系統(tǒng)有比較全面的理解,那么在早期建模過程中就可以發(fā)現(xiàn)被測系統(tǒng)中的一些問題,不需要等到執(zhí)行大量用例時(shí)才發(fā)現(xiàn);?
3、測試自動(dòng)化的水平更高:建模后,MBT可以自動(dòng)生成測試用例,不需要人工編寫測試文檔;?
4、測試覆蓋率變得更高,使得徹底的測試(即:窮盡測試)成為可能:傳統(tǒng)測試設(shè)計(jì)的過程中依賴人工,MBT生成測試路徑時(shí)會(huì)避免人工設(shè)計(jì)測試用例時(shí)的思維局限性,生成更豐富的測試路徑用例,但是是否需要執(zhí)行“徹底”的測試由漏測對業(yè)務(wù)的影響決定。MBT只是從技術(shù)上提供了窮盡測試可能性。?
5、基于模型間接維護(hù)測試用例的方式更高效:傳統(tǒng)維護(hù)時(shí)需要依賴人工,需要耗費(fèi)大量的人力和時(shí)間成本,重新測試設(shè)計(jì)。使用MBT只需要維護(hù)好測試模型即可,生成測試用例的工作可以由MBT工具自動(dòng)完成;
缺點(diǎn)
1、學(xué)習(xí)成本較高:要求開發(fā)人員、測試人員都精通建模,和工具的選型;?
2、使用MBT的初期投資較大:已有的MBT工具不一定適合自己產(chǎn)品,定制的話需要考慮擴(kuò)展性,和處理復(fù)雜邏輯,也就是要花費(fèi)大量時(shí)間和精力;?
3、用例爆炸
使用@xstate/test對TodoMVC進(jìn)行基于模型的測試
Todo MVC是前端流行的一個(gè)用于展示現(xiàn)代JavaScript 框架和狀態(tài)管理功能的Web App。它的Spec可以在github.com/tastejs/tod…找到。@xstate/test是由微軟的David Khourshid編寫的一個(gè)基于模型的測試生成工具,它是基于Statecharts這一有限狀態(tài)機(jī)模型衍生出的工具。但它不僅能用于測試使用xstate編寫的軟件,對所有能被模型化的Web App基本都能通過這一工具進(jìn)行基模測試。
使用@xstate/test對某一系統(tǒng)進(jìn)行基于模型的測試有如下幾個(gè)步驟:
1、創(chuàng)建對該應(yīng)用描述的Statecharts抽象模型?
2、通過可視化工具等方法確保模型的一致性?
3、在模型的狀態(tài)中添加確保系統(tǒng)處在該狀態(tài)的斷言?
4、提供應(yīng)用狀態(tài)轉(zhuǎn)換的事件與真實(shí)操作?
5、使用測試框架運(yùn)行自動(dòng)生成的測試
下面我嘗試使用xstate軟件對有一個(gè)Todo項(xiàng)目的情況進(jìn)行簡單建模。該TodoMVC代碼fork自Cypress的官方示例:GitHub - cypress-io/cypress-example-todomvc: The official TodoMVC tests written in Cypress.。
對Todo Item進(jìn)行建模
其中Unknown是一個(gè)中間過渡狀態(tài),它可以確保該Todo狀態(tài)機(jī)正確保存了內(nèi)部的狀態(tài)并進(jìn)行轉(zhuǎn)換??梢詤⒖迹?/p>
https://xstate.js.org/docs/guides/history.htmlhttps://xstate.js.org/docs/guides/transitions.html#transient-transitions該模型的代碼可以在這個(gè)GitHub Gist上看到:https://gist.github.com/iFwu/1abe1fa124ed0fdea4fd96e5bf55ad89

TodoMVC的Todo Item的Statecharts模型——通過xstate可視化工具生成
添加狀態(tài)的斷言
在要測試的狀態(tài)節(jié)點(diǎn)(Unknown的過渡狀態(tài)除外)中添加確保App處在這一狀態(tài)的斷言:
Editing:?{
??meta:?{
????test:?()?=>?{
??????cy.get('.todo-list?li').should('have.class',?'editing');
??????cy.get('.main').should('be.visible');
??????cy.get('.footer').should('be.visible');
????},
??},
}
這里用到了Cypress的選擇器和斷言工具,確保在編輯狀態(tài)時(shí)的UI狀態(tài)是正確的。這樣依次添加完成所有狀態(tài)的斷言。
添加狀態(tài)轉(zhuǎn)換事件
在狀態(tài)模型中定義的抽象狀態(tài)轉(zhuǎn)換事件并不能使測試App的真實(shí)狀態(tài)發(fā)生轉(zhuǎn)換,所以要在測試模型中注入能使該事件正確發(fā)生的UI方法調(diào)用。
使用Cypress提供的函數(shù)產(chǎn)生轉(zhuǎn)換事件
生成用例和運(yùn)行測試
有了上面這些準(zhǔn)備,現(xiàn)在@xstate/test就可以自動(dòng)生成并運(yùn)行測試:
const?testPlans?=?testModel.getSimplePathPlans();
testPlans.forEach((plan)?=>?{
??plan.paths.forEach((path)?=>?{
????it(path.description,?()?=>?{
??????return?cy
????????.get('.new-todo')
????????.type('Hello?World!{enter}')
????????.then(()?=>?{
??????????return?path.test(cy);
????????});
????});
??});
});
同時(shí)你也可以調(diào)用Test Model上的testCoverage方法獲得不同狀態(tài)的覆蓋率報(bào)告:
it('coverage',?()?=>?{
??testModel.testCoverage({
????filter:?(stateNode)?=>?!stateNode.id.startsWith('Todo.Unknown'),
??});
});
使用@xstate/test + cypress進(jìn)行測試的界面

問題與展望
因?yàn)閷υ搼?yīng)用定義的狀態(tài)非常復(fù)雜,所以只能執(zhí)行最短路徑用例生成,不能執(zhí)行簡單路徑用例生成,否則會(huì)由于狀態(tài)太多生成過多測試用例,導(dǎo)致應(yīng)用卡死。
簡單路徑的圖示

最短路徑的圖示

但這也只是Todo MVC最簡單(只有一個(gè)Todo項(xiàng)目)的狀態(tài)機(jī),如果Todo數(shù)量增加,狀態(tài)數(shù)量將呈指數(shù)增長。所以如果要將該測試方法應(yīng)用到實(shí)際,還需要更高階層的抽象或者更加自動(dòng)化的方法來生成測試對象的狀態(tài)模型。
推薦資源
xstate作者關(guān)于Model-Based Testing的演講: https://www.youtube.com/watch?v=tpNmPKjPSFQ使用Learning-Based Testing方法自動(dòng)生成Todo MVC的狀態(tài)模型的文章: https://link.springer.com/chapter/10.1007/978-3-319-68270-9_7
往期推薦


點(diǎn)擊下方閱讀原文加入社區(qū)會(huì)員
點(diǎn)贊鼓勵(lì)一下

