前端自动化测试浅析
前言:測(cè)試簡(jiǎn)介
前端常見(jiàn)的問(wèn)題:
修改某個(gè)模塊功能時(shí),其它模塊也受影響,很難快速定位bug
多人開(kāi)發(fā)代碼越來(lái)越難以維護(hù)
不方便迭代,代碼無(wú)法重構(gòu)
代碼質(zhì)量差
增加自動(dòng)化測(cè)試后:
我們?yōu)楹诵墓δ芫帉?xiě)測(cè)試后可以保障項(xiàng)目的可靠性
強(qiáng)迫開(kāi)發(fā)者編寫(xiě)更容易被測(cè)試的代碼,提高代碼質(zhì)量
編寫(xiě)的測(cè)試有文檔的作用,方便維護(hù)
?
測(cè)試方法可以分為三個(gè)大類黑盒測(cè)試、白盒測(cè)試、灰盒測(cè)試
黑盒測(cè)試一般也被稱為功能測(cè)試,更注重結(jié)果的展示,要求測(cè)試人員將程序看作一個(gè)整體,不考慮其內(nèi)部結(jié)構(gòu)和特性,只是按照期望驗(yàn)證程序是否能正常工作(就是不知道源代碼是什么意思,只是針對(duì)界面、bug的測(cè)試),現(xiàn)在傳統(tǒng)的測(cè)試部門主要采用這種測(cè)試方式,比較片面化,只能測(cè)到看得的東西,一些內(nèi)部復(fù)雜的邏輯可能測(cè)不到。
白盒測(cè)試是基于代碼本身的測(cè)試,一般指對(duì)代碼邏輯結(jié)構(gòu)的測(cè)試,更注重?cái)?shù)據(jù)的流動(dòng),注重過(guò)程。
灰盒測(cè)試是一種集合了白盒測(cè)試和黑盒測(cè)試的長(zhǎng)處的測(cè)試方法。
?
相關(guān)概念
TDD
TDD是Test Driven Development 的縮寫(xiě),也就是測(cè)試驅(qū)動(dòng)開(kāi)發(fā)。
通常傳統(tǒng)軟件工程將測(cè)試描述為軟件生命周期的一個(gè)環(huán)節(jié),并且是在編碼之后。但敏捷開(kāi)發(fā)大師Kent Beck在2003年出版了 Test Driven Development By Example 一書(shū),從而確立了測(cè)試驅(qū)動(dòng)開(kāi)發(fā)這個(gè)領(lǐng)域。
TDD需要遵循如下規(guī)則:
寫(xiě)一個(gè)單元測(cè)試去描述程序的一個(gè)方面。
運(yùn)行它應(yīng)該會(huì)失敗,因?yàn)槌绦蜻€缺少這個(gè)特性。
為這個(gè)程序添加一些盡可能簡(jiǎn)單的代碼保證測(cè)試通過(guò)。
重構(gòu)這部分代碼,直到代碼沒(méi)有重復(fù)、代碼責(zé)任清晰并且結(jié)構(gòu)簡(jiǎn)單。
持續(xù)重復(fù)這樣做,積累代碼。
TDD具有很強(qiáng)的目的性,在直接結(jié)果的指導(dǎo)下開(kāi)發(fā)生產(chǎn)代碼,然后不斷圍繞這個(gè)目標(biāo)去改進(jìn)代碼,其優(yōu)勢(shì)是高效和去冗余的。所以其特點(diǎn)應(yīng)該是由需求得出測(cè)試,由測(cè)試代碼得出生產(chǎn)代碼。打個(gè)比方就像是自行車的兩個(gè)輪子,雖然都是在向同一個(gè)方向轉(zhuǎn)動(dòng),但是后輪是施力的,帶動(dòng)車子向前,而前輪是受力的,被向前的車子帶動(dòng)而轉(zhuǎn)。
BDD
所謂的BDD行為驅(qū)動(dòng)開(kāi)發(fā),即Behaviour Driven Development,是一種新的敏捷開(kāi)發(fā)方法。其實(shí)可以認(rèn)為BDD是TDD的一個(gè)子集或分支,是測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的升級(jí)版,它更趨向于需求,需要共同利益者的參與,強(qiáng)調(diào)用戶故事(User Story)和行為。2009年,在倫敦發(fā)表的“敏捷規(guī)格,BDD和極限測(cè)試交流”中,Dan North對(duì)BDD給出了如下定義:
BDD是第二代的、由外及內(nèi)的、基于拉(pull)的、多方利益相關(guān)者的(stakeholder)、多種可擴(kuò)展的、高自動(dòng)化的敏捷方法。它描述了一個(gè)交互循環(huán),可以具有帶有良好定義的輸出(即工作中交付的結(jié)果):已測(cè)試過(guò)的軟件。
它對(duì)TDD的理念進(jìn)行了擴(kuò)展,在TDD中側(cè)重點(diǎn)偏向開(kāi)發(fā),通過(guò)測(cè)試用例來(lái)規(guī)范約束開(kāi)發(fā)者編寫(xiě)出質(zhì)量更高、bug更少的代碼。而B(niǎo)DD更加側(cè)重設(shè)計(jì),其要求在設(shè)計(jì)測(cè)試用例的時(shí)候?qū)ο到y(tǒng)進(jìn)行定義,倡導(dǎo)使用通用的語(yǔ)言將系統(tǒng)的行為描述出來(lái),將系統(tǒng)設(shè)計(jì)和測(cè)試用例結(jié)合起來(lái),從而以此為驅(qū)動(dòng)進(jìn)行開(kāi)發(fā)工作。
大致過(guò)程:
從業(yè)務(wù)的角度定義具體的,以及可衡量的目標(biāo)
找到一種可以達(dá)到設(shè)定目標(biāo)的、對(duì)業(yè)務(wù)最重要的那些功能的方法
然后像故事一樣描述出一個(gè)個(gè)具體可執(zhí)行的行為。其描述方法基于一些通用詞匯,這些詞匯具有準(zhǔn)確無(wú)誤的表達(dá)能力和一致的含義。例如,expect, should, assert
尋找合適語(yǔ)言及方法,對(duì)行為進(jìn)行實(shí)現(xiàn)
測(cè)試人員檢驗(yàn)產(chǎn)品運(yùn)行結(jié)果是否符合預(yù)期行為。最大程度的交付出符合用戶期望的產(chǎn)品,避免表達(dá)不一致帶來(lái)的問(wèn)題
?
測(cè)試的分類
單元測(cè)試(Unit Testing)
集成測(cè)試(Integration Testing)
端到端測(cè)試(E2E Testing)
一、單元測(cè)試(Unit Test)
參考鏈接:
https://www.jianshu.com/p/bb713d2fe3ad
1、什么是單元測(cè)試
單元測(cè)試(unit testing),是指對(duì)軟件中的最小可測(cè)試單元進(jìn)行檢查和驗(yàn)證。對(duì)于單元測(cè)試中單元的含義,一般來(lái)說(shuō),要根據(jù)實(shí)際情況去判定其具體含義,如C語(yǔ)言中單元指一個(gè)函數(shù),Java里單元指一個(gè)類,圖形化的軟件中可以指一個(gè)窗口或一個(gè)菜單等。總的來(lái)說(shuō),單元就是人為規(guī)定的最小的被測(cè)功能模塊。單元測(cè)試是在軟件開(kāi)發(fā)過(guò)程中要進(jìn)行的最低級(jí)別的測(cè)試活動(dòng),軟件的獨(dú)立單元將在與程序的其他部分相隔離的情況下進(jìn)行測(cè)試,屬于白盒測(cè)試。
?
對(duì)于JavaScript來(lái)說(shuō),通常也是針對(duì)函數(shù)、對(duì)象和模塊的測(cè)試
2、JavaScript單元測(cè)試現(xiàn)狀
單元測(cè)試在后臺(tái)開(kāi)發(fā)中非常流行和普及,比如JAVA開(kāi)發(fā)者的JUnit等,而在前端開(kāi)發(fā)中則使用的非常少。究其原因,主要是單元測(cè)試更適用于邏輯代碼的測(cè)試,這對(duì)于JAVA等后臺(tái)編程語(yǔ)言來(lái)說(shuō)測(cè)試起來(lái)非常方便,但是前端開(kāi)發(fā)很多時(shí)候要要UI打交道,UI相關(guān)的代碼不是不可以進(jìn)行單元測(cè)試,但的確很麻煩,比起邏輯代碼來(lái)說(shuō)困難多了,這就導(dǎo)致了單元測(cè)試在前端開(kāi)發(fā)沒(méi)有普及起來(lái)。
但是隨著單元測(cè)試的普及,尤其是敏捷開(kāi)發(fā)的推動(dòng),涌現(xiàn)了許多優(yōu)秀的JavaScript單元測(cè)試框架,如QUnit、Jasmine等。所有的這些框架基本上都能對(duì)Javascript代碼進(jìn)行很好的測(cè)試,當(dāng)然UI部分的代碼測(cè)試一樣比較麻煩,但是我們可以通過(guò)精心構(gòu)造我們的測(cè)試代碼來(lái)測(cè)試部分UI代碼。
3、為什么要進(jìn)行單元測(cè)試
經(jīng)驗(yàn)表明一個(gè)盡責(zé)的單元測(cè)試方法將會(huì)在軟件開(kāi)發(fā)的某個(gè)階段發(fā)現(xiàn)很多的Bug,并且修改它們的成本也很低。在軟件開(kāi)發(fā)的后期階段,Bug的發(fā)現(xiàn)并修改將會(huì)變得更加困難,并要消耗大量的時(shí)間和開(kāi)發(fā)費(fèi)用。無(wú)論什么時(shí)候作出修改都要進(jìn)行完整的回歸測(cè)試,在生命周期中盡早地對(duì)軟件產(chǎn)品進(jìn)行測(cè)試將使效率和質(zhì)量得到最好的保證。在提供了經(jīng)過(guò)測(cè)試的單元的情況下,系統(tǒng)集成過(guò)程將會(huì)大大地簡(jiǎn)化。開(kāi)發(fā)人員可以將精力集中在單元之間的交互作用和全局的功能實(shí)現(xiàn)上,而不是陷入充滿很多Bug的單元之中不能自拔。
?
4. 如何進(jìn)行單元測(cè)試
4.1 選擇測(cè)試工具
在JavaScript世界中,我們需要至少三個(gè)工具來(lái)進(jìn)行單元測(cè)試,這意味著每個(gè)工具都需要你進(jìn)行選擇:
測(cè)試管理工具測(cè)試管理工具是用來(lái)組織和運(yùn)行整個(gè)測(cè)試的工具,它能夠?qū)y(cè)試框架、斷言庫(kù)、測(cè)試瀏覽器、測(cè)試代碼和被測(cè)試代碼組織起來(lái),并運(yùn)行被測(cè)試代碼進(jìn)行測(cè)試。測(cè)試工具有很多選擇,Selenium、WebDriver/Selenium 2、Mocha[1]、JsTestDriver、HTML Runners和Karma,我這里選擇使用Karma。
karma:Google Angular 團(tuán)隊(duì)寫(xiě)的,功能很強(qiáng)大,有很多插件。可以連接真實(shí)的瀏覽器跑測(cè)試。能夠用一些測(cè)試覆蓋率統(tǒng)計(jì)的工具統(tǒng)計(jì)一下覆蓋率;或是能夠加入持續(xù)集成,提交代碼后自動(dòng)跑測(cè)試用例。
?
測(cè)試框架測(cè)是框架是單元測(cè)試的核心,它提供了單元測(cè)試所需的各種API,你可以使用它們來(lái)對(duì)你的代碼進(jìn)行單元測(cè)試。JavaScript的測(cè)試框架可謂百花齊放,選擇太多了(可以參考List of unit testing frameworks),我這里選擇使用Mocha(關(guān)于它們中一些框架的對(duì)比,可以參考javascript單元測(cè)試)
Jasmine:自帶斷言(assert),mock功能
Mocha:框架不帶斷言和mock功能,需要結(jié)合其他工具,由tj大神開(kāi)發(fā)
Jest:由Facebook出品的測(cè)試框架,在Jasmine測(cè)試框架上演變開(kāi)發(fā)而來(lái)
?
斷言庫(kù)斷言庫(kù)提供了用于描述你的具體測(cè)試的API,有了它們你的測(cè)試代碼便能簡(jiǎn)單直接,也更為語(yǔ)義化,理想狀態(tài)下你甚至可以讓非開(kāi)發(fā)人員來(lái)撰寫(xiě)單元測(cè)試。所謂的斷言,就是預(yù)期某些執(zhí)行結(jié)果符合你自己的要求。所有的測(cè)試用例都應(yīng)該含有一句或多句的斷言。當(dāng)然,你也完全可以不使用斷言庫(kù),而是用自己的測(cè)試代碼去測(cè)試,不過(guò)幾乎沒(méi)有人會(huì)這么干,除非你自己實(shí)現(xiàn)了一個(gè)測(cè)試斷言庫(kù)。測(cè)試斷言庫(kù)的選擇也不少:better-assert、should.js、expect.js、chai.js等等(有關(guān)它們的對(duì)比,可以參考幾款前端測(cè)試斷言庫(kù)(Assertions lib)的選型總結(jié))我這里選擇chai.js。
chai: 目前比較流行的斷言庫(kù),支持 TDD(assert),BDD(expect、should)兩種風(fēng)格
should.js:也是tj大神所寫(xiě)
mock庫(kù)
參考鏈接:
https://www.jianshu.com/p/bb713d2fe3ad
sinon.js:使用Sinon,我們可以把任何JavaScript函數(shù)替換成一個(gè)測(cè)試替身。通過(guò)配置,測(cè)試替身可以完成各種各樣的任務(wù)來(lái)讓測(cè)試復(fù)雜代碼變得簡(jiǎn)單。支持 spies, stub, fake XMLHttpRequest, Fake server, Fake time,很強(qiáng)大
?
有了上面的四個(gè)工具,你就可以開(kāi)始對(duì)你的node代碼進(jìn)行測(cè)試了。但是如果想要對(duì)前端代碼進(jìn)行測(cè)試,還需要另外一個(gè)工具:
測(cè)試瀏覽器前端代碼是運(yùn)行在瀏覽器中的,要對(duì)其進(jìn)行單元測(cè)試,只能將其運(yùn)行在瀏覽器上。目前大部分測(cè)試工具都支持調(diào)用和運(yùn)行本地瀏覽器來(lái)進(jìn)行測(cè)試,但如果你的測(cè)試僅僅是針對(duì)函數(shù)和模塊的單元測(cè)試,則完全可以使用一款無(wú)界面的瀏覽器:PhantomJs
另外,還有一個(gè)很重要的事情就是測(cè)試覆蓋率的統(tǒng)計(jì)。一般情況下你的測(cè)試管理工具會(huì)提供相關(guān)的覆蓋率統(tǒng)計(jì)工具,但是有些情況下它們提供的工具未必是你想要的。比如當(dāng)被測(cè)試的代碼是經(jīng)過(guò)了某些打包工具打包完了且被壓縮和混淆了,同時(shí)打包工具還混入了很多自己的代碼,這時(shí)覆蓋率的統(tǒng)計(jì)就容易不準(zhǔn)確。所以為了避免這種情況,測(cè)試覆蓋率統(tǒng)計(jì)工具需要謹(jǐn)慎選擇,至少你得確認(rèn)它支持你的打包工具已經(jīng)打包好的代碼。
?
測(cè)試覆蓋率統(tǒng)計(jì)工具
Karma-Coverage是Karma官方提供的覆蓋率統(tǒng)計(jì)插件,自然成為項(xiàng)目的首選。
?
參考網(wǎng)站:
https://www.cnblogs.com/baoshuyan66/p/9937087.html
?
karma-webpack?用webpack預(yù)處理文件
karma-coverage?測(cè)試覆蓋率
karma-mocha?接入mocha測(cè)試框架
karma-spec-reporter?輸出報(bào)告
karma-phantomjs-launcher?控制PhantomJS
karma-phantomjs-shim?給PhantomJS兼容的控制
?
4.2?構(gòu)建一個(gè)最基本的測(cè)試
參考網(wǎng)站:
https://www.jianshu.com/p/6726c0410650#fn1
?
describe('index.js的測(cè)試', function () {it('1應(yīng)該是數(shù)字', function() {// expect(isNum(1)).to.be.trueisNum(1).should.equal(true)})it('"1" 應(yīng)該是字符', function() {// expect(isString('1')).to.be.trueisString('1').should.equal(true)})})describe塊稱為"測(cè)試套件"(test suite),表示一組相關(guān)的測(cè)試。它是一個(gè)函數(shù),第一個(gè)參數(shù)是測(cè)試套件的名稱("index.js的測(cè)試"),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)。
it塊稱為"測(cè)試用例"(test case),表示一個(gè)單獨(dú)的測(cè)試,是測(cè)試的最小單位。它也是一個(gè)函數(shù),第一個(gè)參數(shù)是測(cè)試用例的名稱("1應(yīng)該是數(shù)字"),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)。
?
其中,describe和it是mocha的語(yǔ)法結(jié)構(gòu),describe是這對(duì)某個(gè)組件或者函數(shù)的名字描述,測(cè)試腳本里面應(yīng)該包括一個(gè)或多個(gè)describe塊,每個(gè)describe塊應(yīng)該包括一個(gè)或多個(gè)it塊。it是對(duì)它需要完成某些功能的描述,它里面是具體的測(cè)試用例。在測(cè)試框架中,describe,it,?expect和sinon都是全局方法。
expect(isString('1')).to.be.true是Chai下的語(yǔ)法?
二、集成測(cè)試(Integration Testing)
1、什么是集成測(cè)試
?
將已測(cè)試過(guò)的單元測(cè)試函數(shù)進(jìn)行組合集成暴露出的高層函數(shù)或類的封裝,對(duì)這些函數(shù)或類進(jìn)行的測(cè)試,屬于白盒測(cè)試
選用Travis CI(免費(fèi))或Circle CI來(lái)完成持續(xù)集成測(cè)試
本地根目錄創(chuàng)建名為 .travis.yml 的Travis配置文件
?
阮一峰教程:
http://www.ruanyifeng.com/blog/2017/12/travis_ci_tutorial.html
?
三、端到端測(cè)試(E2E Testing)
參考鏈接:
https://blog.csdn.net/sinat_33312523/article/details/82955514
1、什么是端到端測(cè)試
End to End Testing, 打開(kāi)應(yīng)用程序模擬輸入,檢查功能以及界面是否正確,像是一個(gè)自動(dòng)化的測(cè)試腳本,模擬用戶行為。在Web應(yīng)用程序中,他們會(huì)啟動(dòng)服務(wù)器,打開(kāi)瀏覽器,去自動(dòng)點(diǎn)擊一個(gè)真實(shí)瀏覽器環(huán)境中的頁(yè)面,再通過(guò)直接抓取頁(yè)面上的DOM來(lái)斷言是否符合預(yù)期,這是最接近用戶的測(cè)試方式,從一定程度而言端到端測(cè)試對(duì)于一個(gè)產(chǎn)品的發(fā)布起到了至關(guān)重要的作用,這直接關(guān)系到了DOM給用戶帶來(lái)的視覺(jué)上的交互。不同于行為驅(qū)動(dòng)測(cè)試(BDD)和單元測(cè)試獨(dú)立運(yùn)行并使用模擬/存根,端到端測(cè)試將試著盡可能從用戶的視角,對(duì)真實(shí)系統(tǒng)的訪問(wèn)行為進(jìn)行仿真。對(duì)Web應(yīng)用來(lái)說(shuō),這意味著需要打開(kāi)瀏覽器、加載頁(yè)面、運(yùn)行JavaScript,以及進(jìn)行與DOM交互等操作。屬于灰盒測(cè)試
2、框架介紹
官網(wǎng):https://www.cypress.io/
Cypress是基于 electron 的一個(gè)測(cè)試框架,提供 web 環(huán)境進(jìn)行點(diǎn)對(duì)點(diǎn)的測(cè)試,在 programer 思維下,進(jìn)行自動(dòng)化的交互操作,必要點(diǎn)檢測(cè)說(shuō)明,這是一個(gè)非常棒的用處。例如之后擁有數(shù)據(jù)埋點(diǎn),可以在固定的位置檢測(cè)是否有埋點(diǎn)。
?
2.1 Cypress四個(gè)特性
?
時(shí)間旅行
Cypress在您的測(cè)試運(yùn)行時(shí)拍攝快照。只需將鼠標(biāo)懸停在命令日志中的命令上即可確切了解每一步中發(fā)生的情況。不僅僅是每個(gè)測(cè)試用例都會(huì)出現(xiàn)在測(cè)試頁(yè)面的左側(cè),每一次瀏覽器的行為都會(huì)被記錄在一個(gè)類似Timeline的列表中,在鼠標(biāo)移入后右側(cè)頁(yè)面將會(huì)停留在一個(gè)那一個(gè)瞬間形成一個(gè)快照庫(kù),我認(rèn)為這是一個(gè)非常棒的功能,這更適合我們來(lái)定位bug所發(fā)生的時(shí)間點(diǎn)和位置。
?
可調(diào)試
不用再去猜測(cè)為什么你的調(diào)試失敗了。因?yàn)檫@款框架是基于electron來(lái)編寫(xiě)的,所以用來(lái)承載它運(yùn)行的為Chrome瀏覽器,也就是說(shuō)我們的應(yīng)用是跑在一個(gè)真實(shí)的瀏覽器的環(huán)境中,所以在我們的頁(yè)面出現(xiàn)問(wèn)題的時(shí)候,我們同樣可以調(diào)用熟悉的F12控制臺(tái)去debug
?
實(shí)時(shí)重新加載
不論你何時(shí)更改了測(cè)試內(nèi)容,Cypress都會(huì)自動(dòng)熱加載,這樣你就能隨時(shí)在應(yīng)用中看到所執(zhí)行的命令
?
自動(dòng)等待
Cypress會(huì)自動(dòng)等待命令和斷言。這也是一個(gè)非常討喜的功能,在使用Jest中,我時(shí)常需要調(diào)用Vue自身所攜帶的異步等待函數(shù)vue.nextTick來(lái)進(jìn)行下一步的斷言。在Cypress中我們完全不需要擔(dān)心異步等待在代碼中的呈現(xiàn),框架會(huì)自動(dòng)等待回調(diào)的結(jié)束,在這個(gè)過(guò)程中并且會(huì)啟動(dòng)一個(gè)計(jì)時(shí)。
?
2.2 Cypress配置安裝
目錄結(jié)構(gòu)
?
在根目錄下還需要一個(gè)cypress.json
{"pluginsFile": "tests/e2e/plugins/index.js","projectId": "8efjtr" }?
我們這里一個(gè)個(gè)來(lái)介紹文件的目錄結(jié)構(gòu)和配置說(shuō)明
?
cypress.json
首先在啟動(dòng)整個(gè)Cypress時(shí),會(huì)在項(xiàng)目的根目錄中去尋找這個(gè)文件,在vue-cli-3中單獨(dú)把pluginsFile這個(gè)配置項(xiàng)指向了我們重新配置的路徑(實(shí)際在安裝完一個(gè)Cypress項(xiàng)目時(shí),測(cè)試用例所在的目錄就是cypress文件夾),在這個(gè)被指向的配置文件中再去使用config參數(shù)配置其他目錄所在的位置:
//?https://docs.cypress.io/guides/guides/plugins-guide.html module.exports = (on, config) => {return Object.assign({}, config, {// baseUrl: "http://localhost:8080", // 測(cè)試域名fixturesFolder: 'tests/e2e/fixtures', integrationFolder: 'tests/e2e/specs', // 測(cè)試文件文件夾screenshotsFolder: 'tests/e2e/screenshots', // 屏幕快照// videoRecording: true,videosFolder: 'tests/e2e/videos', // 錄制后的文件夾supportFile: 'tests/e2e/support/index.js'}) }當(dāng)然我們也可以直接在cypress.json中去指定這些配置:
{"projectId": "by9ntm","fixturesFolder": "test/e2e/fixtures","integrationFolder": "test/e2e/specs","pluginsFile": "test/e2e/plugins","screenshotsFolder": "test/e2e/screenshots","supportFile": "test/e2e/support","videosFolder": "test/e2e/videos","baseUrl": "http://localhost:8080" }生成配置后可以在項(xiàng)目啟動(dòng)后的dashboard的setting選項(xiàng)中看到所有我們對(duì)配置的改動(dòng),并會(huì)通過(guò)不同顏色進(jìn)行標(biāo)注。同樣這個(gè)json的文件還可以對(duì)不同環(huán)境下的配置做不同的更改,具體參照官方文檔。
?
config
在這個(gè)文件夾中我主要做了一個(gè)當(dāng)前環(huán)境的判斷配置,由于Cypress是瀏覽器真實(shí)去訪問(wèn)一個(gè)鏈接的地址,所以需要把整個(gè)項(xiàng)目給啟動(dòng)后才可以去測(cè)試,所以需要一個(gè)準(zhǔn)確的baseUrl
fixtures
這個(gè)文件夾很有意思,是為了存放模擬上傳或讀取的文件,在場(chǎng)景中我們經(jīng)常會(huì)碰到文件的拖拽上傳或者下載等等,這些操作在單元測(cè)試是非常難以測(cè)試的,所以我們需要在端到端測(cè)試中把這些操作模擬掉,這個(gè)文件夾就是用來(lái)存放這類特殊文件的。
specs
顧名思義所有的測(cè)試用例都要放在這個(gè)下面。
support
還是回到上傳文件這個(gè)場(chǎng)景,很多特殊場(chǎng)景的情況下Cypress的API很可能沒(méi)有覆蓋到,所以可以在這個(gè)文件夾下配置自定義的命令全局注入到框架中使用。
?
基礎(chǔ)配置
/plugins/index.js
// https://docs.cypress.io/guides/guides/plugins-guide.html module.exports = (on, config) => {return Object.assign({}, config, {// baseUrl: "http://localhost:8080", // 測(cè)試域名fixturesFolder: 'tests/e2e/fixtures', integrationFolder: 'tests/e2e/specs', // 測(cè)試文件文件夾screenshotsFolder: 'tests/e2e/screenshots', // 屏幕快照// videoRecording: true,videosFolder: 'tests/e2e/videos', // 錄制后的文件夾supportFile: 'tests/e2e/support/index.js',viewportHeight: 768, // 測(cè)試瀏覽器視口高度 viewportWidth: 1366 // 測(cè)試瀏覽器視口寬度}) }運(yùn)行測(cè)試
在vue-cli 3.0的默認(rèn)配置中,我們直接運(yùn)行npm run test:e2e來(lái)啟動(dòng)我們的測(cè)試,在這之前我們需要先啟動(dòng)我們的項(xiàng)目和mock服務(wù),最后去啟動(dòng)我們的E2E,否則baseUrl會(huì)請(qǐng)求不到正確的端口。
這是啟動(dòng)頁(yè)面,頂部會(huì)有三個(gè)選項(xiàng)配置可供選擇,在</>Tests選項(xiàng)中我們可以看到我們所有的spec,當(dāng)然這個(gè)順序是按照字母排列的,我們可以點(diǎn)擊Run all specs按鈕來(lái)一次性啟動(dòng)所有的用例,也可以點(diǎn)擊單個(gè)用例來(lái)啟動(dòng)。
?
?
在Runs選項(xiàng)中我們可以看到和CI集成所生成的項(xiàng)目ID
?
在Setting選項(xiàng)中我們可以看到merge之后所有的配置
?
運(yùn)行頁(yè)面
這是一次成功的測(cè)試,可以看到左邊的列表都已經(jīng)呈現(xiàn)一個(gè)綠色的狀態(tài),鼠標(biāo)選中可以看到當(dāng)時(shí)頁(yè)面的一個(gè)狀態(tài)。
?
2.3 語(yǔ)法實(shí)戰(zhàn)
在我們寫(xiě)端到端測(cè)試之前,我們應(yīng)該明確我們是基于一個(gè)用戶的角度去測(cè)試我們的頁(yè)面,所以這無(wú)關(guān)我們的所有源碼,我們應(yīng)該只專注于瀏覽器所呈現(xiàn)給我們的資源,包括頁(yè)面上的element、控制臺(tái)中network中的所有的請(qǐng)求以及導(dǎo)航欄上的url信息,這是我們可以去測(cè)試和觀察到的所有的點(diǎn)。
spec基本結(jié)構(gòu)
?
這里舉一個(gè)最簡(jiǎn)單的例子,和單元測(cè)試一樣,首先要把所有的用例包裹在一個(gè)describe中
?
1、在用例中先用cy.visit()方法訪問(wèn)地址,這里后面只加了/是因?yàn)閎aseUrl已經(jīng)設(shè)置過(guò)了的原因。
2、使用cy.contains()或者cy.get()去抓取DOM并進(jìn)行斷言,Cypress中默認(rèn)包含的斷言庫(kù)為Chai。
3、由于設(shè)有異步等待的機(jī)制,所以我們可以毫無(wú)顧及地去寫(xiě)下一步的操作,包括button的點(diǎn)擊事件和跳轉(zhuǎn)之后url的判斷。
?
生命周期
在一個(gè)測(cè)試集合中,我們也可以加入自身的生命周期,這些生命周期主要是針對(duì)每個(gè)測(cè)試用例來(lái)執(zhí)行的,包括beforeEach、beforeAll、afterEach、afterAll
我在這個(gè)測(cè)試集合中主要用到了beforeEach這個(gè)聲明周期,在每個(gè)測(cè)試用例開(kāi)始之前我都對(duì)我需要的DOM進(jìn)行抓取并取一個(gè)別名,這樣我方便其他用例需要時(shí)就不需要再反復(fù)去尋找這個(gè)節(jié)點(diǎn)對(duì)象了。
beforeEach(() => {cy.visit('/#/steps/selectMode')cy.get('.one__item__right').eq(0).find('.item__right__btn').eq(0).as('hasConfigFile')cy.get('.one__item__right').eq(0).find('.item__right__btn').eq(1).as('notConfigFile')cy.get('.one__item__right').eq(1).find('.item__right__btn').eq(0).as('hasSystem')cy.get('.one__item__right').eq(1).find('.item__right__btn').eq(1).as('notSystem')cy.get('.btn__next').as('next')cy.get('.item__upload__text').as('fileName')})在取了別名之后其他用例只需要調(diào)用cy.get('@name')就可以取到相應(yīng)別名的DOM元素。
模擬請(qǐng)求
在我們測(cè)試的時(shí)候總是會(huì)免不了一些請(qǐng)求的發(fā)出,在Cypress中由于是真實(shí)的瀏覽器環(huán)境,所以所有的請(qǐng)求都會(huì)被正常發(fā)出,但是有些時(shí)候我們需要mock掉一些請(qǐng)求來(lái)觀察DOM的反饋是否符合預(yù)期,這里就需要引入一個(gè)比較重要的概念——存根stub。
不同于單元測(cè)試的mock,我認(rèn)為在單元測(cè)試中更類似于axios中的攔截器,對(duì)整個(gè)請(qǐng)求的代碼層面進(jìn)行一個(gè)攔截后返回一個(gè)相同格式的對(duì)象騙過(guò),而在端到端測(cè)試中因?yàn)槲覀儫o(wú)法對(duì)項(xiàng)目本身的源碼下手,所以我們只能從瀏覽器層面去模擬,在這里的存根我的理解是在頁(yè)面發(fā)出請(qǐng)求之前,先對(duì)一個(gè)API做一個(gè)標(biāo)記,當(dāng)瀏覽器觸發(fā)這個(gè)方法并發(fā)送請(qǐng)求后使用標(biāo)記后的模擬請(qǐng)求返回并進(jìn)行后續(xù)的斷言操作,我們來(lái)看一下代碼。
describe('installSystem', () => {it('尋找節(jié)點(diǎn)失敗', () => {cy.server()cy.route({method: 'DELETE',url: 'api/find/node',status: 200,response: {data: {},error_code: 1,message: 'error'}})cy.visit('/#/steps/installSystem')cy.wait(1000)cy.get('.pop_content_confirm').find('div').find('div').contains('尋找節(jié)點(diǎn)出錯(cuò)')}) })在這個(gè)例子中,由于請(qǐng)求在頁(yè)面剛被掛載后就被觸發(fā)了,也就是說(shuō)整個(gè)請(qǐng)求是寫(xiě)在mounted這個(gè)聲明周期中的,所以我們需要在訪問(wèn)頁(yè)面之前就對(duì)這個(gè)需要被mock的api做一個(gè)stub。
?
1、首先我們使用cy.server()聲明一個(gè)mock的請(qǐng)求。
2、然后使用cy.route()去描述我們需要模擬的api的具體信息,在里面可以填寫(xiě)很多的配置,包括請(qǐng)求的方法method,請(qǐng)求的地址url,請(qǐng)求返回的狀態(tài)碼status以及最后返回的response body,在這里由于項(xiàng)目本身中還定義了error_code狀態(tài)碼,所以對(duì)于這一個(gè)請(qǐng)求所具備的狀態(tài)我們就需要寫(xiě)很多個(gè)測(cè)試用例的組合去斷言是否符合我們的預(yù)期。
3、然后因?yàn)檎?qǐng)求已經(jīng)被我們存根了,我們?cè)偃ナ褂胏y.visit()訪問(wèn)一次頁(yè)面就可以看到我們所需要被模擬的請(qǐng)求已經(jīng)被存根并且成功模擬了。
?
?
到目前為止我們就已經(jīng)非常成功地對(duì)一個(gè)API進(jìn)行模擬請(qǐng)求了,對(duì)比起單元測(cè)試還是方便了不少的。
總結(jié)
- 上一篇: 纯CSS实现React Logo图形,内
- 下一篇: 仅使用HTML和CSS实现的标签云效果