緊耦合金融系統群的測試自動化策略(一)
科技子公司或者IT部門在一個大金融團體里面只能算是個成本中心,對IT團隊來說,核心使命就是穩定運營、降低成本。這對于自動化測試來 說,意味著非常有限的資源預算、不穩定的測試環境、復雜的系統耦合關系、嚴苛的測試數據要求,還有那近乎無理的信息安全規范。如此種種,讓我們并不能按照 自己想象的那樣去實施自己的規劃,以致會走很多彎路;而再回首你會覺得,有時候只是方法欠妥而不是資源不夠,有時候只是導向錯誤而非技術不夠高。
關鍵字:金融系統;自動化測試;單元測試;環境監控;測試數據;持續集成;
傳統金融系統
以往,很多傳統金融企業所使用的系統依賴采購和外包開發、維護的占比較大,這可能會讓他們自己的IT隊伍的經驗少一些,而對商業工具、產品和解決方案的依賴性較強。而現在,這些IT隊伍要么在新技術和互聯網的壓力下做艱難的轉型,要么還在堅持著古老的體制文化,要么非常年輕,經驗不足。他們的開發流程也有可能會臃腫僵化,也可能會做敏捷做得些敏而不捷,也可能測試自動化水平低下,也可能太過冒進——在我看來,這個行業里優秀的IT隊伍遠少于外面精彩的大世界,但是,無論是主動地還是被動地,他們都在隨著時代在慢慢改變。
對于金融核心業務系統來說,大部分都是將前端按照業務屬主的部門劃分而做模塊化設計。例如我們的保險系統,被拆分為網銷、新契約、保全、理賠、渠道人 管、財務、電銷與客服、查詢與報表等等很多子系統;銀行則可能會有客戶管理、存貸款、外匯、國際結算、渠道、報表等等,有更多的模塊。而無論前端如何拆 分,一個子公司的業務數據和大部分核心業務邏輯在后端都是共享的,所以整個系統群的基礎業務數據和業務邏輯都是緊密耦合的。
在這個行業 里,我體會到,為了領先同業,倉促啟動和上線的產品項目不勝枚舉。因為這些項目的經營策略相關性或者政治敏感性較高,所以無論在DeadLine之前實現 了多少,只要沒有致命問題,總要如期上線,而把遺留的問題通過后續排期解決。這種快餐式的設計和實現讓很多項目和產品看似風光的按期上線,都附帶了相當多 的系統債務的產生:緊耦合、難重構、難以自動化測試、運維成本高。而且這種債務是增量累積的,沒有額外的人力很難清理掉。要么在日常工作之外付出額外的努力,要么任其自生自滅,造就“前人宿醉、后人埋單”的奇觀。
在當今綜合金融的大潮中,這些子系統必須滿足的條件不僅是數據在系統群內共享,還要與集團其他子公司共享、與監管部門共享,以后也許會和醫院與醫療機 構、社保機構甚至其他政府部門共享。看起來在我們現有的架構下,綜合金融就意味著更多、更復雜的耦合,對測試來說就意味著測試數據使用難度的提升,從而對 測試的要求更高。同互聯網一樣,如今的金融產品也需要搶占市場先機,也需要快速的發布,但是快速發布不等于快餐式開發,否則久而久之系統維護債務的累積遲 早將達到無法負擔的地步。而謝絕快餐式的發布,就需要新的開發模式,作為開發流程中的一個重要環節,軟件測試也在被逼無奈地隨著改變、轉型,來面臨新的要求。
時之沙聚金字塔
我們反復被教導,測試要越早越好:越早發現,修復成本就越低,所以就有了個分層測試的金字塔概念,而這個理論也經過很多優秀的公司的成功實踐和論證,我 們不懷疑其正確性。但如果要做更多的單元測試,就需要代碼有足夠好的可測性,而現有的系統動輒就有著幾十萬、幾百萬行的陳舊代碼,跨系統的邏輯調用比比皆 是,離傳說中的高內聚、低耦合相去甚遠。所以,在我們的實際操作中看起來,要做多少單元測試就要做多少代碼重構,而做多少代碼重構就需要多少能夠守護代碼 重構行為的測試。在這種情況下,只做代碼重構或者只做單元測試的編寫都是不靠譜的,這看起來是個死循環。
但是,不僅通過單元測試自動化 能夠達成質量守護,通過GUI去做的自動化測試和手動測試也能夠做到。雖然長期使用通過業務展示層去做測試自動化的成本很高,但是它的建設可以快速完成。 同時須知,同步做代碼重構和單元測試編寫的風險很大,如果在做的時候沒有穩定的質量守護,作為涉費的金融核心業務系統,這種發布風險是不能被接受的。所以 比較現實的路看起來只有一條路:快速建設好GUI層驅動的自動化測試,用它來守護代碼重構和單元測試。
我想借用這個沙漏圖來說明我的觀點:推動這種系統測試的 轉型,不能幻想一蹴而就的單元測試的建設,可以考慮通過GUI來做測試自動化,將其做扎實,以其為基礎來推動這些陳舊的系統群的自動化測試轉型。而接下 來,要慢慢地用單元測試自動化來逐步替換這個基于用戶展示層的測試自動化,直到它們之間形成一個合理的比例。在測試水平提高和轉型的過程中,每個類型的測 試都有可能成為瓶頸;尤其是通過業務展示層來做的自動化測試,沒有它,代碼重構和單元測試自動化也無法穩步地推動。在現實中的人力配比下,聚沙成塔對我們 來說是一個神話,信念支持我們朝著目標持續邁進,但是在短時間內卻不易企及。
對絕大多數人來說,談到單元測試,第一個沖入腦海的是“覆蓋”這兩個字,而其中部分人對覆蓋率這三個字的關注度要遠遠高于四種覆蓋設計方法。假 設我們的目標測試范圍內有N個功能,平均每個功能有9個邏輯分支(含Exception分支)。如果要考量單元測試的覆蓋方法,我想絕大多數人會選擇每個 功能點選擇少于4個的主要分支去覆蓋。我的確也在公司的持續集成郵件組里看到這種爭論,參與討論的開發同事無一例外地贊同這種做法,他們主要考慮的因素是 ROI,他們認為在非常有限的時間里,將單個功能點的分支覆蓋太多并沒有太多意義,因為用戶經常使用的是主要的那兩三個分支。這種看法似乎符合常理,但實 際上并沒有他們想象的那么經濟,我實際上并不指望他們現在這個階段做多么好的單元測試,但是卻不愿意看到他們按照這種想法做下去。
我們不妨把每個功能覆蓋主要分支,覆蓋盡可能多的功能點的覆蓋取向稱做橫向覆蓋;對于每個功能點,爭取覆蓋盡可能多分支,而不計較有限的時間內 覆蓋了多少功能點的覆蓋取向叫做縱向覆蓋。我個人的觀點是:盡量采取縱向覆蓋,自動化測試這同軍事機械化作戰一樣,大縱深要比長戰線好。我之所以在這種系 統群的測試轉型過程中,對單元測試建設推崇縱向覆蓋策略,理由如下:
1)時間短、人力少,短時間無法覆蓋全面,無論縱橫,雙方在這一點上的論據一致。
2)一個功能的N個分支,如果在設計單元測試的時候只覆蓋其中少數分支,在后續進行更深入覆蓋的時候可能需要重構所有的測試以保證測試代碼的邏 輯一致性和可維護性,這是一種重復工作的浪費。換言之,與被測代碼一樣,測試用例不能習慣于采取快餐式設計,而應該在一開始就針對該功能全面設計好。
3)對于這些待重構代碼來說,全面考量一個功能點盡可能多的分支的覆蓋,能夠驅動更加完美的重構,反之,測試的設計與開發一旦分批進行,必然招致額外的重構工作。
4)至于有些開發認為在短時間內覆蓋主要分支主要是因為用戶平常只用那么幾個主要的分支功能,我覺得恰恰相反。我們可以挑選用戶使用最多的功能 而不是所有功能的主要分支,因為主要功能里面的“非主要分支”甚至是Exception分支同樣有發生致命故障的可能。例如,對個退費轉賬,如果在某個異 常中沒有處理好,可能會出轉賬數據反復生成的情況,這樣的資金流入個人賬戶之后是基本再也無法追回的。這種故障即便快速恢復,也遠比那些次要的功能不可用 招致的損失大。
5)有些人認為,那些不重要的分支,在UI回歸測試的時候著重測試一下就好了,這更是違背金字塔模型核心的經濟原則。要知道UI回歸測試的強項 在于對主流程的覆蓋,而分支和異常通過它去覆蓋本身就是非常難的事情;既然認同單元測試的經濟性,還要依賴GUI測試去做那些非常難以實現的分支的測試, 這豈不是自相矛盾么?
6)此外,這種縱深覆蓋更能鍛煉測試代碼編寫者的測試思維,完整的經驗比被阻斷的實踐更具參考價值。后續編寫新的功能模塊的代碼時,會因為做測 試設計時近乎完整的分支考量而更加能夠兼顧所有的分支和異常。單元測試是一種高效的編碼能力提升的手段,而在做單元測試的時候將分支的全面覆蓋,則是高效 的設計技能的提升手段。
單元測試覆蓋的縱與橫的概念只是一家之言,無需糾結概念。我其實只是希望大家在考慮“覆蓋率”這三個字之前都去考慮一下覆蓋的策略,而不要吧眼 光只放在那些乏味的覆蓋率統計數字上。做測試規劃,好的方略比優秀的代碼更加能解決實際問題;寧可讓沙子漏得慢一點,也不要為了暫時看起來流得快而對沙子 中混入的石子視而不見,否則遲早會因為石子堵住瓶口而無法繼續聚沙成塔的夢想。
耦合關系的處理
對于我們討論的這種系統,有人說mock對單元測試來說不是銀彈,濫用mock會影響交互點的驗證,降低測試的有效性。對這種看法,我深以為然,而且單元測試且如此,通過GUI做的測試就更不用說了,盲目的解耦會大大提高為測試有效性所付出的代價。
拿我們一個較為單純的系統為例,中間價是weblogic,數據庫是oracle,部署邏輯關系如下:
1)Browser通過HTTP訪問單點認證系統、用戶管理系統、影像系統、打印平臺與印章系統等;
2)Web服務通過TCP訪問單點認證系統,通過LDAP訪問OID,通過NFS訪問NAS,通過T3訪問App服務;
3)App服務通過JDBC訪問數據庫,通過T3訪問用戶管理系統、工作流、單證系統、后援集中錄入系統和集團公網前置系統,通過LDAP訪問OID,通過NFS訪問NAS……
4)核心DB通過TJS/ETL/GoldenGate與集團其他應用數據庫相連。
那么是否要在通過GUI去測試的時候始終保持所有這種平臺級耦合的測試環境穩定呢?根據我們的經驗,答案是肯定的,因為如果通過GUI去做測 試,這種耦合關系是保證被測功能完整性的基本條件。無論這些平臺或系統有著多么頻繁的構建與發布,在我們通過GUI去做測試的時候都要保持一個穩定可用的 版本,對于我們的系統的持續集成,這種需求更甚。我曾經說過:在通過GUI快速構建的時候要徹底mock掉對關聯系統的依賴,這徹底二字其實并不貼切,我 本意所指只是系統群內的關聯關系而已。而理論終歸是理論,根據我們公司的實際流程,我又把這種系統群內耦合關系的mock分為兩種:
自動化BVT/冒煙:在持續集成頻繁的構建中,由于系統群內業務邏輯和數據的共用,關聯系統的版本移交時間和頻率不定,無法要求其保持穩定不變的版本以供關聯測試,需要將這部分關聯mock掉。
自動化回歸測試:在同一個系統群內,所有版本發布的最終日期是一致的,故爾最終的回歸測試都將在一個集中的時段內完成,而這段時間內基本所有的版本都已經趨于穩定。在這種狀態下,出于測試全面和有效性的考量,自動化回歸測試將不mock任何關聯。
那么同一套測試腳本能否支持這種測試執行需求呢?我們借鑒了功能開關的特性,通過采集版本計劃信息進行推算,做出一個判斷是否回歸測試的公共接 口。在測試腳本中調用共用接口,得到測試運行信息,決定如何處理對關聯系統的依賴。持續集成對于很多公司或產品來說都不是件很難做的事情,而對于本文所述 的陳舊的緊耦合金融系統群來說卻有一定的難度。我觀察了我們公司一些持續集成做得比較成功的案例,大都是業務源頭系統,如網銷,基本不存在業務數據依賴 性,或是系統群內部關系簡單或者索性就是獨立不成群的系統。除了實施手段較為高明和付出的努力較多之外,最重要的是他們成功地規避了這里提到的緊耦合的問 題,或者根本就沒有遇到這種問題。所以我個人的見解是:在通過GUI去做自動化測試的過程中,要確保平臺級耦合系統環境的穩定性,分場景地mock系統群 內部的耦合關系,而不能一概而論。
監控輔助的測試
有人糾結測試自動化和自動化測試之間的差別,簡單舉個例子說明一下個人的理解:在測試環境搭建監控系統,用其輔助自動化測試運行,這個行為總的 來說可以稱作測試自動化。而自動化測試主要的內容則是自動化(主要指腳本化)的測試執行動作,這二者之間還是有點差別的。如果非要咬文嚼字,我覺得其差異 在于自動化測試行為必定包含verifying,而測試自動化行為則可以只有甚至沒有checking。如果將測試自動化的行為或體系在運用的時候加上 verifying,就可能變成自動化測試。
提到測試環境監控,我覺得它的價值絕不亞于生產環境的監控,它能夠幫助測試節省很多問題定位的成本,幫助發現一些在頁面上無法發現的異常。根據 個人理解,除去基礎架構統一管理的相關內容,我將其劃分為5個部分(目前并未完成建設):測試環境應用服務器監控、測試環境數據庫狀態監控、其他測試服務 的狀態監控、自動化測試運行相關監控、業務系統邏輯健康度檢查。用環境監控與自動化測試相互輔佐,可以發現更多的潛在問題,下面講兩個簡單的例子來說一下 自動化測試和測試環境監控的關系。
例1、數據傳輸自動測試(未親自實施)
很多時候,我們會使用幾種商業工具和一些其他的企業級應用。在日常的測試工作中,針對這些工具或它們的特性也需要做大量的測試。例如,在平安科 技,單就數據傳輸管理而言,會用到:ESB/EAI/TJS、ETL(DataStage)、GoldenGate(Oracle)等。因為這些工具使用 的頻率非常大,所以有想法的人就想著把這部分工作內容也做成自動化或者半自動化。
這樣一來,可能有人就把這個自動化變成了對這些service和job工作狀態的checking,同時仍然簡稱這是在做自動化測試,并且試圖 說明這樣的測試通過就能夠保證不出Level-1級別的故障事件。不過我認為,如果只是對這些service和job的基本可用性做checking,而 不是verifying,那么這部分自動化工作做成測試環境的一個監控即可,而不是自動化測試,而且這種checking是無法保證不出Level-1級 別事件的。
例2、報表生成自動測試(指導同事實施中)
我們的查詢系統分三類:實時查詢系統、綜合報表系統和MIS系統,時效從高到低。實時查詢系統自動化測試較為簡單;而MIS系統由于維度和指標 非常復雜,暫時采用手動測試的方法。而這個綜合報表系統有數百長單一維度的報表,每張報表使用一個獨立的存儲過程來生成,生成的結果文件含xls、 csv、zip、html等各多文件類型。報表之間的關聯影響幾乎可以忽略,而報表的正確性則會受其他公用邏輯改動的影響。此外,這種報表介于oltp和 olap之間,生成的效率從幾秒到幾小時不等,提交之后由quartz控制生成異步任務,是測試自動化的難題之一。
因為測試執行的機器資源有限,所以一般來說自動化測試的運行都是實時的,但是報表生成的時長卻長短不一。理論上,如果不想占用太多資源,只能依 賴有輪詢機制的工具或者系統來檢查它運行的結果。恰好,我們的監控系統就有這種機制,而且既然監控系統能夠檢查報表是否已經生成完畢并觸發郵件發送,它就 一定可以觸發對其結果正確性校驗的自動化測試程序的運行。我們可以考慮在監控平臺配置一個新的監控和一組規則,隨后實現這種報表系統的測試:
1)建立被測報表生成的存儲過程代碼的基線版本,存放于自動化測試的基線版本庫中。
2)將自動化測試代碼分為自動化報表生成和自動化報表結果校驗兩部分。
3)在任何變更引發的綜合報表系統測試中,使用基線版本代碼運行報表,取得報表結果文件;在最新的被測報表代碼版本上,用自動化GUI測試再度生成這些報表,并取得結果文件。
4)使用監控系統輪詢報表生成情況,已經完成的報表則調用其自動化結果校驗的代碼再度運行,解析并比對兩次獲得的結果文件,進行報告反饋。
5)被測報表邏輯發生變更,則更新被測報表基線代碼。
很顯然,這種系統的自動化測試,決不是單靠一段單純的自動化測試代碼的運行就能解決的,輔助的方法或許有很多,但是善用已有的資源,無疑是一個很好的辦法。如上文所述,監控系統的checking行為由于它聯動了自動化的verifying動作,就變成了自動化測試。
(未完待續)
posted on 2013-03-21 10:25 順其自然EVO 閱讀(377) 評論(0) 編輯 收藏 所屬分類: 敏捷測試