由于數(shù)據(jù)應(yīng)用開(kāi)發(fā)和功能性軟件系統(tǒng)開(kāi)發(fā)存在很大的不同,在我們實(shí)踐過(guò)程中,在開(kāi)發(fā)人員和質(zhì)量保證人員間常常有大量關(guān)于測(cè)試如何實(shí)施的討論。下文將嘗試總結(jié)一下數(shù)據(jù)應(yīng)用開(kāi)發(fā)的特點(diǎn),并討論在這些特點(diǎn)之下,對(duì)應(yīng)的測(cè)試策略應(yīng)該是怎么樣的。
功能性軟件的測(cè)試
先來(lái)回顧一下功能性軟件系統(tǒng)開(kāi)發(fā)中的測(cè)試。
測(cè)試一般分為自動(dòng)化測(cè)試和手工測(cè)試。由于手工測(cè)試對(duì)人工依賴程度很高,如果主要依賴手工測(cè)試來(lái)保證軟件質(zhì)量,將無(wú)法滿足軟件快速迭代上線的需要?,F(xiàn)代軟件開(kāi)發(fā)越來(lái)越強(qiáng)調(diào)自動(dòng)化測(cè)試的作用,這也是敏捷軟件開(kāi)發(fā)的基本要求。有了全方位的自動(dòng)化測(cè)試保障,就有可能做到每周上線,每日上線甚至隨時(shí)上線。
這里主要討論自動(dòng)化測(cè)試。
測(cè)試金字塔
我們一般會(huì)按照如下測(cè)試金字塔的原則來(lái)組織自動(dòng)化測(cè)試。
測(cè)試金字塔分為三層,自下而上分別對(duì)應(yīng)單元測(cè)試、集成測(cè)試、端到端測(cè)試。
單元測(cè)試是指函數(shù)或類級(jí)別的,較小范圍代碼的測(cè)試,一般不依賴外部系統(tǒng)(可通過(guò)Mock或測(cè)試替身等實(shí)現(xiàn))。單元測(cè)試的特點(diǎn)是運(yùn)行速度非??欤ㄗ詈萌吭趦?nèi)存中運(yùn)行),所以執(zhí)行這種測(cè)試的成本也就很低。單元測(cè)試在測(cè)試金字塔的最底端,占的面積最大。這指導(dǎo)我們應(yīng)該構(gòu)建大量的這類測(cè)試,并以這類測(cè)試為主來(lái)保證軟件質(zhì)量。
集成測(cè)試是比單元測(cè)試集成程度更高的測(cè)試,它在運(yùn)行時(shí)執(zhí)行的代碼路徑更廣,通常會(huì)依賴數(shù)據(jù)庫(kù)、文件系統(tǒng)等外部環(huán)境。由于依賴了外部環(huán)境,集成測(cè)試的運(yùn)行速度更慢,執(zhí)行測(cè)試的成本更高。集成測(cè)試在測(cè)試金字塔的中間,這指導(dǎo)我們應(yīng)該構(gòu)建中等數(shù)量的這類測(cè)試。集成測(cè)試在Web應(yīng)用場(chǎng)景中也常常被稱為服務(wù)測(cè)試(Service Test)或API測(cè)試。
端到端測(cè)試是比集成測(cè)試更靠后的測(cè)試,通常通過(guò)直接模擬用戶操作來(lái)構(gòu)建這樣的測(cè)試。由于需要模擬用戶操作,所以它常常需要依賴一整套完整集成好的環(huán)境,這樣一來(lái),其運(yùn)行速度也是最慢的。端到端測(cè)試在Web應(yīng)用場(chǎng)景中也常常被稱為UI測(cè)試。端到端測(cè)試在測(cè)試金字塔的頂端,這指導(dǎo)我們應(yīng)該構(gòu)建少量的這類測(cè)試。
測(cè)試的范圍非常廣,實(shí)施方法也非常靈活。哪里是重點(diǎn)?我們要在哪里發(fā)力?測(cè)試金字塔為我們指明了方向。
進(jìn)入測(cè)試金字塔
為了更深入地理解一般軟件的測(cè)試要怎么做,我們需要進(jìn)一步深入分析一下測(cè)試金字塔。
測(cè)試帶來(lái)的信心
上文中的金字塔圖示有一個(gè)特點(diǎn)并沒(méi)有反映出來(lái),那就是,越上層的測(cè)試給團(tuán)隊(duì)帶來(lái)的信心越強(qiáng)。這還算好理解,試想,如果沒(méi)有單元測(cè)試,只有端到端測(cè)試,我們是不是可以認(rèn)為程序大部分還是可以正常工作的(可能存在一些邊界場(chǎng)景有問(wèn)題)?但是如果只有單元測(cè)試而沒(méi)有端到端測(cè)試,我們連程序能不能運(yùn)行都不知道!
端到端測(cè)試能帶來(lái)很強(qiáng)的信心,但這常常構(gòu)成另一個(gè)陷阱。由于端到端測(cè)試對(duì)團(tuán)隊(duì)有很大的吸引力,一些團(tuán)隊(duì)可能會(huì)選擇直接構(gòu)建大量的端到端測(cè)試而忽略單元測(cè)試。這些端到端測(cè)試運(yùn)行緩慢,一般也難以修改,很快就會(huì)讓團(tuán)隊(duì)舉步維艱。緩慢的測(cè)試帶來(lái)了緩慢的持續(xù)集成,高頻率的上線就慢慢變得遙不可及。
單元測(cè)試雖然不能直接給人很強(qiáng)的信心,但是常常是更有效的測(cè)試手段,因?yàn)樗梢院苋菀椎母采w到各種邊界場(chǎng)景。
測(cè)試金字塔是敏捷軟件開(kāi)發(fā)所推崇的測(cè)試原則,它是在測(cè)試帶來(lái)的信心和測(cè)試本身的可維護(hù)性兩者中權(quán)衡做出的選擇。測(cè)試金字塔可以指導(dǎo)我們構(gòu)建足夠的測(cè)試,使得團(tuán)隊(duì)既對(duì)軟件質(zhì)量有足夠的信心,又不會(huì)有太多的測(cè)試維護(hù)負(fù)擔(dān)。
既然是權(quán)衡,那么我們是否可以以單元測(cè)試和集成測(cè)試為主,而根本不構(gòu)建端到端測(cè)試(此時(shí)端到端測(cè)試的功能通過(guò)手工測(cè)試完成)呢?
測(cè)試集成度
對(duì)于一些沒(méi)有UI(或者說(shuō)GUI)的應(yīng)用,或者一些程序庫(kù)、框架(如Spring)等,很多時(shí)候測(cè)試金字塔中的三類測(cè)試并不直接適用。我們可以這樣理解:測(cè)試金字塔并非只是三層,它更多的是幫我們建立了在項(xiàng)目中組織測(cè)試的原則。
事實(shí)上,對(duì)于通用的軟件測(cè)試,我們可以理解為存在一個(gè)集成度的屬性。沿著金字塔往上,測(cè)試的集成度越高(依賴外部組件越多)。由于集成度更高,測(cè)試過(guò)程所要運(yùn)行的代碼就更多更復(fù)雜,測(cè)試運(yùn)行時(shí)間就越長(zhǎng),測(cè)試構(gòu)建和維護(hù)成本就越高。實(shí)踐過(guò)程中,為了提高軟件質(zhì)量和可維護(hù)性,我們應(yīng)當(dāng)構(gòu)建更多集成度低的測(cè)試。
有了測(cè)試集成度的理解,我們就可以知道,其實(shí)金字塔可以不是三層,它完全可以是兩層或者四層、五層。這取決于我們?cè)趺磩澏骋活悳y(cè)試的范圍。同時(shí),我們還可以知道,其實(shí)單元測(cè)試、集成測(cè)試與端到端測(cè)試其實(shí)并沒(méi)有特別明顯的界限。
下面,我們從測(cè)試集成度的角度來(lái)看如何構(gòu)建單元測(cè)試。
上文提到,測(cè)試最好通過(guò)Mock或測(cè)試替身等實(shí)現(xiàn),從而可以不依賴外部系統(tǒng)。但是,如果測(cè)試Mock或測(cè)試替身難以構(gòu)造,或者構(gòu)造之后我們發(fā)現(xiàn)測(cè)試代碼和產(chǎn)品代碼耦合非常嚴(yán)重,這時(shí)應(yīng)該怎么辦呢?一個(gè)可能的選擇是考慮使用更高集成度的測(cè)試。
Spark程序就是這樣的一個(gè)例子。一旦使用了Spark的DataFrame API去編寫代碼,我們就幾乎無(wú)法通過(guò)Mock Spark的API或構(gòu)造一個(gè)Spark測(cè)試替身的方式編寫測(cè)試。這時(shí)的測(cè)試就只能退一步選擇集成度更高一些的測(cè)試,比如,啟動(dòng)一個(gè)本地的Spark環(huán)境,然后在這個(gè)環(huán)境中運(yùn)行測(cè)試。
此時(shí),上面的測(cè)試屬于哪種測(cè)試呢?如果我們用三層測(cè)試金字塔的測(cè)試劃分來(lái)看待問(wèn)題,就很難給這樣的測(cè)試一個(gè)準(zhǔn)確的定位。不過(guò),通常我們無(wú)需考慮這樣的分類,而是可以把它當(dāng)做集成度低的測(cè)試,即金字塔靠底端的測(cè)試。如果團(tuán)隊(duì)成員能達(dá)成一致,我們可以稱其為單元測(cè)試,如果不能,稱其為Spark測(cè)試也并非不可。
何時(shí)停止測(cè)試
所以,對(duì)于一般的軟件測(cè)試,我們可以認(rèn)為測(cè)試策略應(yīng)當(dāng)符合一般意義的金字塔。金字塔的細(xì)節(jié),比如應(yīng)該有幾層塔,每一層的范圍應(yīng)該是什么樣,每一層應(yīng)該用什么樣的測(cè)試技術(shù)等等,這些問(wèn)題需要根據(jù)具體的情況進(jìn)行抉擇。
在討論一般軟件的測(cè)試時(shí),需要關(guān)注軟件的測(cè)試何時(shí)停止,即,如何判斷軟件測(cè)試已經(jīng)足夠了呢?
在老馬的《重構(gòu) 第二版》中,有對(duì)于何時(shí)停止測(cè)試的觀點(diǎn):
有一些測(cè)試規(guī)則建議會(huì)嘗試保證我們測(cè)試一切的組合,雖然這些建議值得了解,但是實(shí)踐中我們需要適可而止,因?yàn)闇y(cè)試達(dá)到一定程度之后,其邊際效用會(huì)遞減。如果編寫太多測(cè)試,我們可能因?yàn)楣ぷ髁刻蠖鴼怵H。我們應(yīng)該把注意力集中在最容易出錯(cuò)的地方,最沒(méi)有信心的地方。
一些測(cè)試的指標(biāo),如覆蓋率,能一定程度上衡量測(cè)試是否全面而有效,但是最佳的衡量方式可能來(lái)自于主觀的感受,如果我們覺(jué)得對(duì)代碼比較有信心,那就說(shuō)明我們的測(cè)試做的不錯(cuò)了。
主觀的信心指數(shù)可能是衡量測(cè)試是否足夠的重要參考。如果要問(wèn)測(cè)試是否足夠,我們要自問(wèn)是否有信心軟件能正常工作。
在實(shí)踐過(guò)程中,我們還可以嘗試分析每次bug出現(xiàn)的原因,如果是由于大部分bug是由于代碼沒(méi)有測(cè)試覆蓋而產(chǎn)生的,此時(shí)我們可能應(yīng)該編寫更多的測(cè)試。但如果是由于其他的原因,比如需求分析不足或場(chǎng)景設(shè)計(jì)不完備而導(dǎo)致的,則應(yīng)該在對(duì)應(yīng)的階段做加強(qiáng),而不是一味的去添加測(cè)試。
數(shù)據(jù)應(yīng)用的測(cè)試
有了前面對(duì)測(cè)試策略的分析,我們來(lái)看看數(shù)據(jù)應(yīng)用的測(cè)試策略。
數(shù)據(jù)應(yīng)用相比功能性軟件有很大的不同,但數(shù)據(jù)應(yīng)用也屬于一般意義上的軟件。數(shù)據(jù)應(yīng)用有哪些特點(diǎn),應(yīng)該如何針對(duì)性的做測(cè)試呢?下面我們來(lái)探討一下這幾個(gè)問(wèn)題。
根據(jù)前面的文章分析,數(shù)據(jù)應(yīng)用中的代碼可以大致分為四類:基礎(chǔ)框架(如增強(qiáng)SQL執(zhí)行器)、以SQL為主的ETL腳本、SQL自定義函數(shù)(udf)、數(shù)據(jù)工具(如前文提到的DWD建模工具)。
基礎(chǔ)框架的測(cè)試
基礎(chǔ)框架代碼是數(shù)據(jù)應(yīng)用的核心代碼,它不僅邏輯較為復(fù)雜,而且需要在生產(chǎn)運(yùn)行時(shí)支持大量的ETL的運(yùn)行。誰(shuí)也不想提交了有問(wèn)題的基礎(chǔ)框架代碼而導(dǎo)致大規(guī)模的ETL運(yùn)行失敗。所以我們應(yīng)當(dāng)非常重視基礎(chǔ)框架的測(cè)試,以保證這部分代碼的高質(zhì)量。
基礎(chǔ)框架的代碼通常由Python或Scala編寫,由于Python和Scala語(yǔ)言本身都有很好的測(cè)試支持,這十分有利于我們做測(cè)試。
基礎(chǔ)框架的另一個(gè)特點(diǎn)是它通常沒(méi)有GUI。
按照測(cè)試金字塔原理,我們應(yīng)當(dāng)為其建立更多的集成度低的測(cè)試(下文稱單元測(cè)試)以及少量的集成度高的測(cè)試(下文稱集成測(cè)試)。
比如,在前面的文章中,我們?cè)鰪?qiáng)了SQL的語(yǔ)法,加入了變量、函數(shù)、模板等新的語(yǔ)法元素。在運(yùn)行時(shí)進(jìn)行變量替換、函數(shù)調(diào)用等等功能通過(guò)基礎(chǔ)框架實(shí)現(xiàn)。這部分功能邏輯較為復(fù)雜,應(yīng)當(dāng)建立更多的單元測(cè)試及少量的集成測(cè)試。
ETL腳本的測(cè)試
ETL腳本的測(cè)試可能是數(shù)據(jù)應(yīng)用中的最大難點(diǎn)。
采用偏集成的測(cè)試
ETL腳本一般基于SQL實(shí)現(xiàn)。SQL本身是一個(gè)高度定制化的DSL,如同XML配置一樣。
XML要如何測(cè)試?很多團(tuán)隊(duì)可能會(huì)直接忽略這類測(cè)試。但是用SQL編寫的ETL代碼有時(shí)候還是可以達(dá)到幾百行的規(guī)模,有較多的邏輯,不測(cè)試的話難以給人以信心。如何測(cè)試呢?
如果采用基于Mock的方法寫測(cè)試,我們會(huì)發(fā)現(xiàn)測(cè)試代碼跟產(chǎn)品代碼是一樣的。所以,這樣做意義不大。
如果采用高集成度的測(cè)試方式(下文稱集成測(cè)試),即運(yùn)行ETL并比對(duì)結(jié)果,我們將發(fā)現(xiàn)測(cè)試的編寫和維護(hù)成本都較高。由于ETL腳本代碼本身可能是比較簡(jiǎn)單且不易出錯(cuò)的,為了不易出錯(cuò)的代碼編寫測(cè)試本身就必要性不高,更何況測(cè)試的編寫和維護(hù)成本還比較高。這就顯得集成測(cè)試這一做法事倍功半。
這里可以舉一個(gè)例子。比如對(duì)于一個(gè)分組求和并排序輸出的SQL,它的代碼可能是下圖這樣的。
可見(jiàn)這兩種測(cè)試方式都不是好的測(cè)試方式。
測(cè)試構(gòu)建原則
那么有沒(méi)有什么好的原則呢?我們從實(shí)踐中總結(jié)出了幾點(diǎn)比較有價(jià)值的思路供大家參考:
(1) 將ETL腳本分為簡(jiǎn)單ETL和復(fù)雜ETL(可以通過(guò)代碼行數(shù),數(shù)據(jù)篩選條件多少等進(jìn)行衡量)。簡(jiǎn)單的ETL通過(guò)代碼評(píng)審或結(jié)對(duì)編程來(lái)保證代碼質(zhì)量,不做自動(dòng)化測(cè)試。復(fù)雜的ETL通過(guò)建立集成測(cè)試來(lái)保證質(zhì)量。
(2) 由于集成測(cè)試運(yùn)行較慢,可以考慮:
- 盡量少點(diǎn)用例數(shù)量,將多個(gè)用例合并為一個(gè)來(lái)運(yùn)行(主要是將數(shù)據(jù)可以合并成單一的一套數(shù)據(jù)來(lái)運(yùn)行)
- 將測(cè)試分級(jí)為需要頻繁運(yùn)行的測(cè)試和無(wú)需頻繁運(yùn)行的測(cè)試,比如可將測(cè)試分級(jí)P0-P5,P3-P5是經(jīng)常(如每天或每次代碼提交)要運(yùn)行的測(cè)試,P0-P2可以低頻(如每周)運(yùn)行
- 開(kāi)發(fā)測(cè)試支持工具,使得運(yùn)行時(shí)可以盡量脫離緩慢的集群環(huán)境。如使用Spark讀寫本地表
(3) 考慮將復(fù)雜的邏輯使用自定義函數(shù)實(shí)現(xiàn),降低ETL腳本的復(fù)雜度。對(duì)自定義函數(shù)建立完整的單元測(cè)試。
(4) 將復(fù)雜的ETL腳本拆分為多個(gè)簡(jiǎn)單的ETL腳本實(shí)現(xiàn),從而降低單個(gè)ETL腳本的復(fù)雜度。
加深對(duì)業(yè)務(wù)和數(shù)據(jù)的理解
我們?cè)趯?shí)踐過(guò)程中發(fā)現(xiàn),其實(shí)大多數(shù)時(shí)候ETL腳本的問(wèn)題不在于代碼寫錯(cuò)了,而在于對(duì)業(yè)務(wù)和數(shù)據(jù)理解不夠。比如,前面文章中的空調(diào)銷售的例子,如果我們?cè)诮y(tǒng)計(jì)銷量的時(shí)候不知道存在退貨或者他店調(diào)貨的業(yè)務(wù)實(shí)際情況,那我們就不知道數(shù)據(jù)中還有一些字段能反映這個(gè)業(yè)務(wù),也就不能正確的計(jì)算銷量了。
想要形成對(duì)數(shù)據(jù)的深入理解需要對(duì)長(zhǎng)時(shí)間的業(yè)務(wù)知識(shí)積累和長(zhǎng)時(shí)間對(duì)數(shù)據(jù)的探索分析(業(yè)務(wù)系統(tǒng)通常經(jīng)歷了長(zhǎng)時(shí)間的發(fā)展,在此期間內(nèi)業(yè)務(wù)規(guī)則復(fù)雜性不斷增加,導(dǎo)致數(shù)據(jù)的復(fù)雜性不斷增加)。對(duì)于剛加入團(tuán)隊(duì)的新人,他們更容易由于沒(méi)有考慮到某些業(yè)務(wù)情況而導(dǎo)致數(shù)據(jù)計(jì)算錯(cuò)誤。
加深對(duì)業(yè)務(wù)和數(shù)據(jù)的理解是進(jìn)行高效和高質(zhì)量ETL腳本開(kāi)發(fā)的必由之路。
有沒(méi)有什么好的實(shí)踐方法可以幫助我們加深理解呢?以下幾點(diǎn)是我們?cè)趯?shí)踐中總結(jié)的值得參考的建議:
- 通過(guò)思維導(dǎo)圖/流程圖來(lái)整理復(fù)雜的業(yè)務(wù)流程(或業(yè)務(wù)知識(shí)),形成知識(shí)庫(kù)
- 盡量多的進(jìn)行數(shù)據(jù)探索,發(fā)掘容易忽略的領(lǐng)域業(yè)務(wù)知識(shí),并通過(guò)第一步進(jìn)行記錄
- 找業(yè)務(wù)系統(tǒng)團(tuán)隊(duì)溝通,找出更多的領(lǐng)域業(yè)務(wù)知識(shí),并通過(guò)第一步進(jìn)行記錄
- 如果有條件,可以更頻繁的實(shí)地使用業(yè)務(wù)系統(tǒng),總結(jié)更多的領(lǐng)域業(yè)務(wù)知識(shí),并通過(guò)第一步進(jìn)行記錄
- 針對(duì)第一步搜集到的這些容易忽略的特定領(lǐng)域業(yè)務(wù)流程,設(shè)計(jì)自動(dòng)化測(cè)試用例進(jìn)行覆蓋
SQL自定義函數(shù)的測(cè)試
在基于Hadoop的分布式數(shù)據(jù)平臺(tái)環(huán)境下,SQL自定義函數(shù)通常通過(guò)Python或Scala編寫。由于這些代碼通常對(duì)外部的依賴很少,通常只是單純的根據(jù)輸入數(shù)據(jù)計(jì)算得到輸出數(shù)據(jù),所以對(duì)這些代碼建立測(cè)試是十分容易的事。事實(shí)上,我們很容易實(shí)現(xiàn)100%的測(cè)試覆蓋率。
在組織測(cè)試時(shí),我們可以用單元測(cè)試的方式,不依賴計(jì)算框架。比如,以下Scala編寫的自定義函數(shù):
對(duì)其建立測(cè)試時(shí),可以直接測(cè)試內(nèi)部的轉(zhuǎn)換函數(shù)array_join_f,一些示例的測(cè)試場(chǎng)景比如:
在建立了單元測(cè)試之后,一般還需要考慮建立少量的集成測(cè)試,即通過(guò)Spark框架運(yùn)行SQL來(lái)測(cè)試此自定義函數(shù),一個(gè)示例可以是:
如果自定義函數(shù)本身十分簡(jiǎn)單,我們也可以直接通過(guò)Spark測(cè)試來(lái)覆蓋所有場(chǎng)景。
從上面的討論可以看出,SQL自定義函數(shù)是很容易測(cè)試的。除了好測(cè)試之外,SQL自定義函數(shù)還有很多好的特性,比如可以很好的降低ETL復(fù)雜度,可以很方便的被復(fù)用等。所以,我們應(yīng)該盡量考慮將復(fù)雜的業(yè)務(wù)邏輯通過(guò)自定義函數(shù)封裝起來(lái)。這也是業(yè)界數(shù)據(jù)開(kāi)發(fā)所建議的做法(大多數(shù)的數(shù)據(jù)開(kāi)發(fā)框架都對(duì)自定義函數(shù)提供了很好的支持,如Hive Presto ClickHouse等,大多數(shù)ETL開(kāi)發(fā)工具也都支持自定義函數(shù)的開(kāi)發(fā))。
數(shù)據(jù)工具的測(cè)試
數(shù)據(jù)工具的實(shí)例可以參考文章《數(shù)據(jù)倉(cāng)庫(kù)建模自動(dòng)化》和《數(shù)據(jù)開(kāi)發(fā)支持工具》。
這些工具的一大特點(diǎn)是,它們是用于支持ETL開(kāi)發(fā)的,僅在開(kāi)發(fā)過(guò)程中使用。由于它們并不是在產(chǎn)品環(huán)境中運(yùn)行的代碼,所以我們可以降低對(duì)其的質(zhì)量要求。
這些工具通常只是開(kāi)發(fā)人員為了提高開(kāi)發(fā)效率而編寫的代碼,存在較大的修改和重構(gòu)的可能,所以,過(guò)早的去建立較完善的測(cè)試必要性不高。
在我們的實(shí)踐過(guò)程中,這類代碼通常只有很少的測(cè)試,我們只對(duì)那些特別復(fù)雜、沒(méi)有信心能正確工作的地方建立單元測(cè)試。如果這些工具代碼是通過(guò)TDD的方式編寫的,通常其測(cè)試會(huì)更多一些。
在持續(xù)集成流水線中運(yùn)行測(cè)試
前面我們討論了如何針對(duì)數(shù)據(jù)應(yīng)用編寫測(cè)試,還有一個(gè)關(guān)于測(cè)試的重要話題,那就是如何在持續(xù)交付流水線中運(yùn)行這些測(cè)試。
在功能性軟件項(xiàng)目中,如果我們按照測(cè)試金字塔的三層來(lái)組織測(cè)試,那么在流水線中一般就會(huì)對(duì)應(yīng)三個(gè)測(cè)試過(guò)程。
從上面的討論可知,數(shù)據(jù)應(yīng)用的測(cè)試被縱向分為四條線,如何對(duì)應(yīng)到流水線上呢?如果我們采用同一個(gè)代碼庫(kù)管理所有的代碼,可以考慮直接將流水線分為四條并行的流程,分別對(duì)應(yīng)這四條線。如果是不同的代碼庫(kù),則可以考慮對(duì)不同的代碼庫(kù)建立不同的流水線。在每條流水線內(nèi)部,就可以按照單元測(cè)試、低集成測(cè)試、高集成測(cè)試這樣的方式組織流水線任務(wù)。
1.獨(dú)立的ETL流水線
對(duì)于ETL代碼的測(cè)試,有一個(gè)值得思考的問(wèn)題。那就是,ETL腳本之間通常獨(dú)立性非常強(qiáng),相互之間沒(méi)有依賴。這是由于ETL代碼常常由完善的領(lǐng)域特定語(yǔ)言SQL開(kāi)發(fā)而成,與Python或Scala等通用編程語(yǔ)言編寫的代碼不同,SQL文件之間是沒(méi)有依賴的(如果說(shuō)有依賴,那也是通過(guò)數(shù)據(jù)庫(kù)表產(chǎn)生依賴)。
既然如此,假設(shè)我們修改了某一個(gè)ETL文件的代碼,是不是我們可以不用運(yùn)行其他的ETL文件的測(cè)試呢?其實(shí)不僅如此,我們甚至可以單獨(dú)上線部署此ETL,而不是一次性部署所有的ETL。這在一定程度上還降低了部署代碼帶來(lái)的風(fēng)險(xiǎn)。
有了上面的發(fā)現(xiàn),我們可能要重新思考數(shù)據(jù)應(yīng)用的持續(xù)交付流水線組織形式。
一個(gè)可能的辦法是為每一個(gè)ETL文件建立一個(gè)流水線,完成測(cè)試、部署的任務(wù)。此時(shí)每個(gè)ETL可以理解為一個(gè)獨(dú)立的小程序。
這樣的想法在實(shí)踐中不容易落地,因?yàn)檫@將導(dǎo)致大量的流水線存在(常常有上百條),從而給流水線工具帶來(lái)了很大的壓力。常用的流水線工具,如Jenkins,其設(shè)計(jì)是難以支撐這么大規(guī)模的流水線的創(chuàng)建和管理的。
要如何來(lái)支持上面這樣的ETL流水線呢?可能需要我們開(kāi)發(fā)額外的流水線工具才行。
2.云服務(wù)中的ETL流水線
現(xiàn)在的一些云服務(wù)廠商在嘗試這樣做。他們通常會(huì)提供一個(gè)基于Web的ETL開(kāi)發(fā)工具,同時(shí)會(huì)提供工具對(duì)當(dāng)前的ETL的編寫測(cè)試。此時(shí),ETL開(kāi)發(fā)人員可以在一個(gè)地方完成開(kāi)發(fā)、測(cè)試、上線,這可以提高開(kāi)發(fā)效率。
這類服務(wù)的一個(gè)常見(jiàn)缺點(diǎn)在于它嘗試用一套Web系統(tǒng)來(lái)支持所有的ETL開(kāi)發(fā)過(guò)程,這帶來(lái)了大量繁雜的配置。這其實(shí)是將ETL開(kāi)發(fā)過(guò)程的復(fù)雜性轉(zhuǎn)化為了配置的復(fù)雜性。相比編寫代碼而言,多數(shù)開(kāi)發(fā)人員不會(huì)喜歡這樣的工作方式。(當(dāng)前軟件開(kāi)發(fā)所推崇的是Everthing as Code的做法,嘗試將所有開(kāi)發(fā)相關(guān)過(guò)程中的東西代碼化,從而可以更好的利用成熟的代碼編輯器、版本管理等功能。而Web配置的方式與Everthing as Code背道而馳。)
對(duì)于這些數(shù)據(jù)云服務(wù)廠商提供的數(shù)據(jù)開(kāi)發(fā)服務(wù),如果可以同時(shí)支持通過(guò)代碼和Web界面配置來(lái)實(shí)現(xiàn)數(shù)據(jù)開(kāi)發(fā),那將能得到更多開(kāi)發(fā)者的喜愛(ài)。這在我看來(lái)是一個(gè)不錯(cuò)的發(fā)展方向。
總結(jié)
由于數(shù)據(jù)應(yīng)用開(kāi)發(fā)有很強(qiáng)的獨(dú)特的特點(diǎn)(比如以SQL為主、有較多的支撐工具等),其測(cè)試與功能性軟件開(kāi)發(fā)的測(cè)試也存在很大的不同。
本文分析了如何在測(cè)試金字塔的指導(dǎo)下制定測(cè)試策略。測(cè)試金字塔不僅可以很好的指導(dǎo)功能性軟件開(kāi)發(fā),在進(jìn)行一般意義上的推廣之后,可以很容易得到一般軟件的測(cè)試策略。關(guān)于測(cè)試金字塔,本文分析了測(cè)試帶來(lái)的質(zhì)量信心及測(cè)試集成度,這兩個(gè)概念可以幫助我們更深刻的理解測(cè)試金字塔背后的指導(dǎo)原則。
在最后,結(jié)合我們的實(shí)踐經(jīng)驗(yàn),給出了一些數(shù)據(jù)應(yīng)用中的測(cè)試構(gòu)建實(shí)踐。將數(shù)據(jù)應(yīng)用分為四個(gè)不同模塊來(lái)分別構(gòu)建測(cè)試,可以很好的應(yīng)對(duì)數(shù)據(jù)應(yīng)用中的質(zhì)量要求,同時(shí)保證有較好的可維護(hù)性。最后,我們討論了如何在持續(xù)集成流水線中設(shè)計(jì)測(cè)試任務(wù),留下了一個(gè)有待探索的方向,即如何針對(duì)單個(gè)ETL構(gòu)建流水線。
數(shù)據(jù)應(yīng)用的質(zhì)量保證是不容易做到的,常常需要我們進(jìn)行很多的權(quán)衡取舍才能找到最適合的方式。想要解決這一問(wèn)題,還要發(fā)揮團(tuán)隊(duì)中所有人的能動(dòng)性,多總結(jié)和思考才行。
原文鏈接:??用測(cè)試金字塔指導(dǎo)數(shù)據(jù)應(yīng)用的測(cè)試 - Thoughtworks洞見(jiàn)??