程式設計師可以自己寫測試?還需要測試人員嗎?

2021-09-24 14:19:13 字數 4812 閱讀 2778

在向開發人員介紹單元測試或tdd等工程實踐時,往往可以聽到這樣的疑問。比如:

自己寫的程式,自己無法從另乙個角度測出問題。寫bug的時間都不夠了,哪有時間來寫測試?開發來寫測試了,測試幹什麼?除了核心的**,沒有什麼值得測試的。……

本篇想要通過**這些問題背後的困難,來說明程式設計師怎樣通過編寫自測**更有效率的進行開發。

乙個例子

首先我們看乙個例子。全專案唯一的測試

不止一次,我在各種專案中看到這樣的測試,往往這也是整個工程中唯一乙個測試。

可以看出,開發者認為編寫是有必要的。所以按照「標準」的做法建立了測試目錄,引入junit依賴。並且利用它在開發的初期來驗證某些技術疑問,一般是某些當時還不熟悉的第三方庫,或者資料庫、中介軟體等外部依賴。

簡單而言:「寫測試是應該,但我們的**沒什麼好測的」

測試,不僅僅關於未知

說起測試,往往與未知相關聯。我們通過試驗、除錯、檢測來獲取獲取反饋,不斷調整。

以上圖為例,一般想到的測試,都集中在「已知的未知」這個象限。正如前面的示例**,使用不熟悉的庫帶來未知。程式設計師通過在測試中呼叫和觀察結果來消除未知。

然而,對於自動化測試來說,其實關注點在於已知。

「都已知了,還測試什麼呀?」,也許你會有這樣的疑問。

火柴問題

火柴,這種行將消失的物品。也許現在的小朋友只是在《賣火柴的小女孩》中才得知它的存在。在我小時候,還是時常用到的。那時,也許是工藝問題,或者儲存條件有限,往往一盒火柴好多根都不能點著。

記的那時聽到的笑話:

小明的媽媽讓他去買盒火柴,不一會功夫買回來了。媽媽問:「你試過沒有,能點著嗎?」

「試過啦」,小明很驕傲的說,「每一根我都試了一遍。」

我把這種問題稱為「火柴問題」,往往傳統的質量控制面臨的都是這類問題,有如下限制:

成本,顯然現實中不會有人把所有的火柴拿來測試。不過問題的本質並沒有變,在花費的成本和獲得安全保證的完全性之間取乙個平衡。

事後,造出火柴後才有能否點著的問題,

因而,一次性,成本換取的安全是一次性的,每當乙個批次到來時,以前的測試的付出都成為了沉沒成本。

另一種測試

讓我們來看另一種關於已知的測試。

比如每天出門的時候,我都會自然而然的檢查一遍,手機、鑰匙、錢包。就是個簡單的清單。

清單是關於已知的,只有十分確定的事項才會列入在清單裡。

清單本身很簡單,並不能回答火柴問題這樣的難題。但是不代表它沒有作用。

以出門為例子,有時出門是每天都在做的上班通勤,有時是去面臨某個很大的未知,比如去見乙個陌生的客戶,進行重要談判。

這時如果有個水晶球,告訴你會成功失敗,甚至告訴你怎樣做才能成功,那就太好了。

然而沒有水晶球。

乙個簡單的清單至少保證你不會走在路上才發現忘帶手機。無論未知的挑戰是什麼,忘帶手機基本上不會產生任何幫助。

切換回軟體開發的場景,程式設計師夢想中的完美測試也許能告訴我們未知,甚至未知的未知結果。這在目前還不現實。那麼寫乙個測試確保你在不斷調整中不破壞正確的事情,仍是值得的。

可以看到,這種視角下的驗證,與檢查火柴有所不同:

預防,這種校驗著眼於未來,是為了避免更大損失的投入。

過程中,檢查是做事步驟中的乙個環節。

反覆,越頻繁的行為越有必要進行校驗,校驗的越頻繁潛在收益越大。

假定你是獨自居住,出門前還是鎖門後發現沒帶鑰匙的成本,會有乙個巨大的飆公升。往往檢查列表都是在這種成本拐點前進行的。

應對這種猛增的成本曲線有三種方式:

拉平曲線,通過技術改進使原本難以挽回的決定變得不那麼昂貴。

優化待檢查專案,比如現在出門帶錢包已經不那麼重要了,有手機即可。如果把門換成掃碼開鎖,那麼鑰匙也免了。這樣需要檢查的專案越少,越不容易遺漏。

自動化,比如遺漏了東西就有提醒警報,自然大大降低了犯錯的可能。

自測給程式設計師帶來什麼

敏捷方**的乙個基礎,就是現代軟體開發方式已經使軟體變更的成本曲線大大平緩了。我們可以看看開發者的自測在其中起到的作用。

對照上面兩個列表,可以回想一下

在最近的開發活動中碰到各類錯誤的比例是多少?

由於反饋時間和定位手段不同,解決錯誤花費的時間有何不同?

有多少最初百思不得其解的錯誤,長時間摸排後定位為一行修改即可改正的弱智錯誤?

如果這些錯都在第一時間發現,以明顯的方式報錯會怎麼樣?……

從加快反饋,幫助定位的角度思考,也許你會找到更多值得寫的測試。

記錄常玩遊戲的同學都熟悉要時常存檔,可以讓我們安心挑戰boss,大不了失敗時返回安全點。

那麼**呢?git,svn等**管理工具使可靠的保留**歷史成為可能。然而,如何在歷史中找到安全點呢?(題外話,你有嘗試過git bisect命令麼)

記錄還帶來了另一件事,覆盤。

沒有記錄也就無從系統的進行回顧和改進。對於編碼,我們往往只能看到最終的結果。這大概也是編碼活動在軟體開發「工程——藝術」 圖譜中最偏向與藝術這一極的原因吧。

頻繁提交的**歷史,加上表達行為變化的測試,會使原本大家熟視無睹的程序如實呈現出來。有興趣的話可以看看這篇cyber-dojo設計者的講演,我們甚至僅僅觀察測試變化的情況就可以對一段程式編寫的過程有個大致的了解。

可以通過測試改進的點

把main函式改為測試

有經驗的開發者大多都知道寫出的**都至少要執行驗證一遍。然而執行**有時並不那麼簡單,有的要以特定的方式部署,有的需要複雜的前置流程才能觸及。為了高效的執行**,我們會採用一些手段,比如為目標**增加乙個main函式,這樣就可以直接以希望的輸入執行想要的操作,並觀察結果。

這種除錯技巧可以很容易的用測試來改寫,如下圖所示。

在基本不增加工作量的前提下,帶來如下收益:

明確的分離了除錯**和生產邏輯。

避免誤導後來維護**的人,也防止把測試**發布到生產環境產生隱患。

抹平了「除錯期——維護期」的成本差異。

main方法的往往是在除錯階段使用。開發人員反覆調整輸入、觀察輸出、修正**,直到開發完成。之後這段除錯程式就成為了過去時。後來者無法判斷這段腳手架**是否還符合最新的邏輯,是否可以執行。

而測試**在每次構建時都會自動檢查,保證**保持上次變更後預期的邏輯。為開發者保留了乙個除錯現場,是否「開發完了」並無顯著差異。

測試可以記錄多種用例

使用除錯方式,我們往往在確認完乙個行為後修改輸入,觀察其它行為。因為預期這是一次性的工作。

用測試可以在不同的用例中描述行為的不同側面。方便維護者理解**,也避免了,「咦,這個bug我明明測過呀」的回歸錯誤。

測試明確寫出了期望的行為。

通過assert,測試明確的寫出可以自動判別的行為。而不是main方法中通過肉眼來閱讀理解程式行為。寫出預期會帶來如下改變:

幫助閱讀者理解什麼是**「應該的」行為。

促使開發者思索**的目的是什麼,會怎樣被使用。

自動判斷節省了開發者的注意力,更有效的反饋錯誤,定位錯誤。

用隔離依賴代替除錯「高仿」**

所謂高仿**,是指與實現**非常接近,但是稍有不同的**。

往往在除錯時,目標**並不是純粹的邏輯處理,還會涉及到其它的外部依賴。這些依賴可能要單獨部署配置,甚至根本無法在開發環境獲得。

為了對付這種情況,乙個顯而易見的方法是把目標**copy乙份到除錯**處,修改依賴相關的部分。比如下圖就演示了一段**,需要根據外部依賴判斷執行某操作,並更新資料庫。為了測試執行操作的邏輯,開發者copy了**,注釋掉與環境相關的**。

另一種類似的處理方法,在每次除錯時臨時修改目標**,除錯結束後再恢復。

這種情況,只需要結合mock框架對外部依賴進行模擬,就可以在不改變目標**的情況下在測試中改變**行為。如上圖所示。

這種做法有避免了顯而易見的問題:

copy**方式在經歷修改後,不能保證於實際生產**一致。

臨時修改**有事後忘記恢復的風險。

除此之外,還有些潛移默化的收益:

使隱含的輸入輸出更加明顯了。

比如例子中的**,從外部看起來只有乙個字串輸入乙個字串輸出。通過測試可以明確的看到,事實上輸入還有從外部依賴獲取的布林值,輸出還有對資料庫的操作。

促使**向鬆耦合、單一職責演化。

有時候為了在測試中mock隔離依賴,會需要對實現**稍作重構。短期看來似乎寫測試引發了更多的工作量和變更,但這種變更一般會使**向職責更明確,模組間更鬆耦合的方向改變。促使開發者在設計層面更多的思考。

用測試來增強注釋

適當的注釋能極大的增強**的可維護性。好的注釋描述**在做什麼,而非怎麼做的。

對於複雜結構的處理,往往看**千頭萬緒,摸不著頭腦。注釋裡附上示例資料,馬上讓人對**的大致行為有所掌握。

將這種注釋中的樣例放入測試中,可以:

避免**修改注釋無人維護的問題。

把不同的輸入和對應輸出一一對應起來。

利用自測促進開發

前面說了一些通過自測手段對已有工作方式的改進。事實上在熟悉掌握這些手段後,可以更進一步,主動利用測試來完成原來不能高效做到的事情。

分解「已知的未知」

對於未知的解決方案,有時是由於我們對於相關技術了解有限。也有一種情況,技術方面已經確定,但是由於問題較為複雜,一時看不到解決方法。

面對這種問題,一般的做法是構造式的。也就是說從自己知道的方案出發,看看需要增加什麼來接近目標,增加後調整使整體一致,再次看需要增加什麼……

還有一種分解式的方式。假定已經有了乙個解決方案,從中選取乙個子集,解決這個子集,然後選取下乙個,直到完全解決。測試就很適合在這種方法中對問題進行分解和檢驗。

在最近的一次練習中,我就體會到即使沒有開始編碼,測試也能對解決問題起到幫助。

而我們都知道,以後往往等於永遠也不會。

造成這種狀況的,除了我們固有的弱點,比如拖延、圖省事外,有個很重要的原因是難以評估改變的影響。還記的前面錯誤反饋列表麼?如果幾個月後才會知道有沒有問題的改動,就算再簡單我們也會避免的。這就是遺留**的處境。

眾所周知,不產生bug的最佳方式就是不寫、不修改**。當然這是不現實的。

敏捷時代,測試還需要嗎?

作為乙個測試人員,由於經常接觸到敏捷開發這個概念,所以總是在想,既然有敏捷開發,是否也應該有敏捷測試呢?答案當然肯定的,因為測試嚴格意義上也是屬於開發中的一部分。但是實際工作中呢,其實我們更多的還是只聽到敏捷開發 真正意義上的開發 幾乎沒有人提及敏捷測試,甚至測試在敏捷中被嚴重弱化了,為什麼呢?因為...

自動程式設計軟體面世 我們還需要程式設計師嗎?

來自美國芝加哥的訊息,一家名為care technologies的軟體公司最近向外界公布了其最新的研究成果,一款聲稱可以把應用軟體開 發速度提高四十七倍並可在某種程度上代替程式設計師的自動程式設計軟體。據了解,這款軟體被命 名為olivanova。為了證實這款軟體的功能,care technolog...

怎樣健身最有效?程式設計師們可能還需要這些

程式設計師的工作流程通常像下面這樣 寫 執行 得到錯誤提示 找出錯誤再次寫 日復一日,年復一年。程式設計師的完美主義 經常找茬的特質很容易使自己陷入積極否定的迴圈怪圈中。可是,自帶光芒的程式猿們不可以自暴自棄 自怨自艾 自生自滅 自私自利 自作自受,而是用積極進取精神取代消極思想意識,好身體裡應該住...