青眼看測試

男女有別,儘管許多國家不斷地採取措施提高女性的地位,男女不平等至今還是全世界的難題。同樣地,一個公司無論怎樣努力地標榜對測試的重視,也難以撫慰測試工程師在和開發工程師比較時那種青澀、曖昧、欲語還休的複雜心緒。坦率且勇敢地面對現實,不妨就直說了吧,測試和開發天生存在不可逾越的鴻溝嗎?測試工程師註定低開發工程師一頭嗎?

稍安勿躁,讓我們從測試工程師和開發工程師共同工作的對象程序的構造說起吧。衆所周知,構成程序的兩大要素是過程(Procedure)和數據(Data),正如MIT計算科學教材SICP (Structure and Interpretation of Computer Programs)開篇所講的那樣,程序是抽象的精靈。

“Computational processes are abstract beings that inhabit computers. As they evolve, processes manipulate other abstract things called data.”

因“四人幫“(Gang of Four)的著作(Design Patterns: Elements of Reusable Object-Oriented Software)而名噪一時的軟件設計模式(Software Design Pattern)系統地描述瞭解決一般性問題的可複製的編碼(programming)參考模板,它本身就是最佳開發實踐的抽象。而經典的模式如簡單工廠模式(Simple Factory)、工廠方法模式(Factory Method)、抽象工廠模式(Abstract Factory)、建造者模式(Builder)、適配器模式(Adapter)、策略模式(Strategy)等背後的共同原理不過是抽象在程序設計中的具體應用。

抽象並沒有想象中那樣的抽象。簡單地講,名正則言順,給程序的過程(方法和函數)或數據(類、對象或變量)賦予一個名字,則其他的過程或數據通過引用該名字而重用該過程或數據,這其實就是抽象了。就程序設計而言,隔離是抽象相伴而生的孿生兄弟,彷彿一枚硬幣的正反兩面,有抽象就有隔離,反之亦然。抽象出來的是程序中一般的、可重用的的過程,而其餘的處理千萬種特定情況的過程就是要隔離的過程。過程如此,數據亦然,比如,類的組合與繼承就是數據的抽象和隔離。我們引用SICP中的一個平凡的(Trivial)scheme的例子來說明這種抽象與隔離的藝術:

(define (map proc items)
  (if (null? items)
    nil
    (cons (proc (car items))
      (map proc (cdr items)))))


(define (scale-list items factor)
  map (lambda (x) (* x factor))
    Items))


(scale-list (list 1 2 3 4) 10)
結果是:(10 20 30 40)


(map (lambda (x) (* x x))
  (list 1 2 3 4))
結果是:(1 4 9 16)

map完成了list的遍歷和再組裝,這是list處理的一般的和可重用的過程,即爲可抽象的部分。對list的元素的轉換卻有千萬種情況,而這種不同被隔離在了傳遞給map的過程參數 ((lambda (x) (* x factor))和(lambda (x) (* x x)))中。作爲參數傳遞的過程扮演着數據的角色,這正是所謂的函數型編程語言(Functional Programming Language)如scheme再平常不過的抽象機制。抽象和隔離給程序設計帶來了極大的靈活性。比如說,將list中的數值元素轉換爲它的絕對值,並不需要重新寫一個過程,遍歷list的每個元素,算出絕對值,並把轉換後的元素組成新的list。取而代之的是僅僅把取絕對值的過程作爲參數傳遞給map,這是何等的簡潔便當:

(define (abs-list items factor)
  map (lambda (x) (if (< x 0)
                (- x)
                x))
      Items))

抽象不是爲了抽象而抽象,耍酷或故作高深更不是抽象的目的。抽象是克服程序複雜性的一種計算思維方法,由抽象定義的類似軟件設計模式那樣的結構可以把代碼以可理解的方式有效地組織起來,來保證代碼的可讀性,降低代碼維護的成本。否則,一切混亂的象一堆麥秸,開發百萬行乃至於千萬行代碼的大型軟件系統從項目的角度是無法管控的。從縱向觀察,由抽象帶來的分層描述了程序最一般的結構。比如,軟件低層的基礎設施隱藏了實現的細節,向上層的功能應用提供了透明的抽象接口。因此,層在程序的不同組成模塊建立了抽象屏障,在接口不變的情況下,低層代碼的改變不會影響上層代碼的邏輯和行爲。衆所周知,在互聯網工業界,軟硬件建立在一個經典的分層模型基礎之上,即開放式系統互聯通信參考模型(Open Systems Interconnection model)和相關的TCP/IP協議簇(TCP/IP Protocol Suite)。若沒有定義在該模型上的由硬件到軟件的層次分明的協議標準,不同網絡廠商的設備及各種Client/Server之間就沒有共同語言,互聯互通更無從談起,今天互聯網的繁榮局面也只能在科幻的想象中存在。

層的抽象提高了程序結構設計的模塊化,與此同時,也造就了軟件工程師的角色與分工,不同角色的工程師工作在程序不同的層上:我們作爲一家生產網絡交換機嵌入式操作系統的軟件公司,Linux系統工程師和ASIC/SDK系統工程師工作在最底層,負責外圍設備的驅動程序及交換芯片的統一的配置控制接口;中間層的協議棧工程師開發面向用戶的網絡功能;最上層的應用工程師提供特定應用場景的解決方案。混跡於職場許多年,就我所知,其他公司大概也有類似的根據層的工程師角色的劃分。比如互聯網公司中工程師常見的角色有前端工程師,後端工程師和算法工程師。

那麼,測試工程師是否也工作在程序的某一層呢?恐怕很多測試工程師都不曾仔細思考過這個問題。測試不過就是測試,即使自動化測試也不生產包含在發佈產品中的代碼,更別提從來不用寫代碼的手工測試了。測試工程師莫非是跳出三界外,不在五行中,天不收,地不管的與程序無涉的特別的另類?!實則大謬不然,測試工程師包括做手工測試的測試工程師必須且只能工作在程序的某層上。否則,測試不過是開發的附庸和配角,焉能不委屈?!就敝公司而言,自動化測試的腳本驗證的是產品的網絡功能,調用的是CLI (Command Line Interface)的命令接口;而應用工程師做的很多項目也是基於系統的CLI實現的。因此,測試工程師和應用工程師工作的目標雖大相徑庭,但工作的內容和方式卻並沒有多大區別,不過都是用CLI實現測試的用例或應用的功能。而做手工測試的測試工程師工作的對象也是CLI,不同之處僅是以手工的方式完成自動化測試無法完成或不方便完成的測試用例。因此,我一直認爲,從抽象的角度來看,無論是測試工程師和開發工程師都是程序員,重要的是,測試工程師應該用程序員的方法思考問題和解決問題,象程序員一樣工作。我常常發現,測試工程師在向開發工程師報告BUG的同時,也忙於修改自己測試腳本的BUG。誰說測試工程師不可以是程序員呢?!推而廣之,上溯到公司的執行官乃至於政府的首腦和國家的元首,不都是工作在不同的層上的領導者嗎?不相信嗎?記得《天龍八部》中有這樣一個情節:蕭峯幫助遼帝耶律洪基平判後,賜爵楚王,官封南院大王,勾兌軍國大事。“蕭峯雖然從來沒做過官,但他久爲丐幫幫主,統率羣豪,自有一番威嚴。帶着丐幫豪傑和契丹大豪,其間也無甚差別。……一切均井井有條。”丐幫幫主是民間協會的領袖,南院大王是政府機構的官職,顯然是不同的兩個層,相差甚遠。但是以蕭峯的領導力,“自有一番威嚴”,無論是哪個崗位,喬幫主還是蕭大王,“其間也無甚差別”。這可是扯的太遠了。

沒有比較就沒有傷害,而有差異纔會有比較。假如大家認同測試工程師和開發工程師都是程序員,那麼,測試工程師和開發工程師之間就很自然地談不到高低的差別。本來嘛,程序的層就是層,無論是上層還是下層,只是抽象級別的不同,並沒有什麼貴賤之分。說歸說,而事實上,不僅有些開發工程師常常矯情地自別於測試工程師,不同角色的開發工程師之間相輕的情況也屢見不鮮。在互聯網公司,廣泛地流傳着這樣一種說法,算法工程師最有優越感,比之前端工程師,後端工程師更有優越感。反過來講,前端工程師想做後端工程師,後端工程師想做算法工程師。人性不過如此,在虛榮心的召喚下,這種優越感往往建立在虛幻的感覺和想象之上,自認爲所工作的層具有更高的技術門檻。信有之乎?系統工程師似乎比應用工程師更有優越感。可笑可悲可嘆可厭可鄙的是,竟有工程師爲了自己“優越”的地位,抱殘守缺,象母雞護小雞一樣緊緊地守衛着自己的那一畝三分地兒,絕不允許他人染指。宛如多疑的皇帝,一夕數驚,日夜不安的擔心別人知道了自己的那點兒微末小技兒,被取而代之。既然有技術含量這樣難以逾越的壁壘,還有什麼可驚可怕的呢?可見,所謂的技術門檻這玩意兒並不是什麼可侍的阿物兒。我常常戲稱這樣的井底之蛙爲“小”程序員。

包括測試工程師在內的程序員的工作是徹底的腦力勞動。最高境界的修煉內化於基於數據和邏輯的即計算機科學特有的計算思維方法,包括抽象、分析、推理、遞歸等;最厲害的絕招外化於解決問題的能力和技巧,如簡化、分而治之、轉換成已知、由具體到一般、或由一般到具體、或由一般到具體且由具體到一般分進合擊等等。程序員的修養鍾情於不吝於動腦筋的測試工程師和開發工程師,在設計,架構,編碼和測試的研發實踐中不斷地昇華。這就是我眼中的程序員,不區分角色,測試工程師,開發工程師和架構師,就共同的方法和技巧而言,他們都是抽象意義上的“大”寫的程序員。從一般的眼光看,架構師似乎更高明一點點兒,其實,人人都可以是架構師。編碼不可能不涉及架構,同樣地,測試不可能不涉及架構。你象架構師一樣寫代碼和做測試,那就是架構師,我願意用一個三段論(syllogism)的句式總結如下:程序員即是架構師;測試工程師和開發工程師都是程序員;所以測試工程師和開發工程師都是架構師。因此,江湖中傳說有一種工程師叫做全棧工程師(Full Stack Developer),十八般武藝,樣樣精通。從前端到後端,從系統到應用,從低層到高層,全都拿得起,放得下。寫得了代碼,做得了架構,幹得了測試。上天入地,無所不能,宛如見首不見尾的神龍。問君何能爾?根固而木長,源深而流遠,超然物外,心遠地自偏,無招勝有招。而那些汲汲於一孔江山的“小”程序員的愚在於伐根以求木茂,塞源而欲流長,奚可焉?!

當然,測試工程師不一定非要精於編程之道,但必須熟稔程序員的的思維方法和解決問題的技巧,否則,只是只想只會根據開發工程師的指令亦步亦趨,甘心做個不動腦筋的操作員,如何擔當得起測試工程師的職任?測試常常強調不留死角地覆蓋功能的方方面面,更要求測試工程師能夠從不同的角度思考問題,針對代碼可能的罩門和軟肋,檢查合法輸入,非法輸入和邊界輸入的各種情況。鄙公司的用戶,無論是數據中心網絡還是企業網絡,對我們研發的交換機網絡操作系統的性能和穩定性的要求近乎苛刻,部署在生產網絡的系統必須7X24小時無差錯不宕機。應對這種挑戰並非易事,我們必須系統地管控產品生產鏈的每個環節,包括需求、設計、架構、開發和測試。比如,測試用例必須系統地覆蓋功能的每一個子功能點,請參考我的博客文章“用系統的方法做系統軟件”。我們從事的是專業性很強的網絡行業,測試工程師必須成爲領域的知識專家,纔有可能做到系統地測試。

朱蘭博士認爲,質量是產品或服務的適用性(Fitness for Use)。因此,測試工程師常常需要驗證用戶典型的應用案例,以確保產品適用於特定的應用場景。在此過程中,測試工程師有機會學習面向用戶的廣闊的應用空間,這纔是實打實的實錘實戰經驗,更需要時間的積累。不誇張地說,開發工程師正對着“小橋流水人家”淺唱低吟的時候,測試工程師已經昂首闊步向着星辰大海啓航了。因此,和開發工程師相比較,測試工程師對程序底層的深度的理解雖然有所不及,但對產品應用的寬度的把握卻遠遠過之。假設不論編碼的基本能力,對於某個特定的工業領域,正是這種寬度和深度定義了工程師團隊的技術實力和技術能力,也是量度和其他工程師團隊距離的最好的度規(Metric)。

誠然,研發過程中有效的組織(Well Organized)和良好的紀律(Well Deciplined)是質量的基本保證。畢竟,產品的質量取決於包含在程序中的每一行代碼,而代碼是開發工程師生產的,也是測試工程師驗證的對象。因此,歸根結底,有什麼樣的工程師,就有什麼樣的產品,好的產品質量是程序員當然包括測試工程師以自己的聰明才智創造(幹)出來的。虛比浮誇,乞靈於管理流程的救贖,就好像馬車跑的慢,不是打馬而去打車,吾期期以爲不可!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章