緊耦合金融系統群的測試自動化策略(一)

玄學醬發表於2017-07-10
三句話背景

  科技子公司或者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做自動化測試的時候,要麼懶得去做mock工作,全部依賴所有的測試環境的穩定性;要麼就花功夫把所有的關聯絡統全部mock掉。這兩種做法 都是不妥當的,因為要指望所有環境都穩定無異於買彩票。另一方面,這些平臺級的耦合關係,如果想通過mock的手法來解耦,將付出很大的代價,而程式的正 常功能也無法測試完整。例如,如果要繞過SSO和UM的認證,可能就要分析和執行時修改cookie,而這本身就是不安全的,也不是一定能夠實現的。而 且,如果中介軟體的一個provider配置錯誤,繞過去的測試也不能發現這個問題,尤其是全部依賴自動化測試的情況下。

  那麼是否要在通過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本身可能存在一定的加工和轉換的邏輯(GoldenGate可能會較為單純),自動化測試可能 需要對其內部邏輯做細緻的測試。而對資料加工轉換的邏輯做自動化測試,無異於換個人重寫一套邏輯去驗證原先開發的邏輯,十分複雜,ROI並不高。

  這樣一來,可能有人就把這個自動化變成了對這些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動作,就變成了自動化測試。

  (未完待續)








====================================分割線================================



最新內容請見作者的GitHub頁:http://qaseven.github.io/


相關文章