程式設計師可以自己寫測試?還需要測試人員嗎?
在向開發人員介紹單元測試或TDD等工程實踐時,往往可以聽到這樣的疑問。比如:
自己寫的程式,自己無法從另一個角度測出問題。寫bug的時間都不夠了,哪有時間來寫測試?開發來寫測試了,測試幹什麼?除了核心的程式碼,沒有什麼值得測試的。……
本篇想要透過探討這些問題背後的困難,來說明程式設計師怎樣透過編寫自測程式碼更有效率的進行開發。
一個例子
首先我們看一個例子。全專案唯一的測試
不止一次,我在各種專案中看到這樣的測試,往往這也是整個工程中唯一一個測試。
可以看出,開發者認為編寫是有必要的。所以按照“標準”的做法建立了測試目錄,引入JUnit依賴。並且利用它在開發的初期來驗證某些技術疑問,一般是某些當時還不熟悉的第三方庫,或者資料庫、中介軟體等外部依賴。
簡單而言:“寫測試是應該,但我們的程式碼沒什麼好測的”
測試,不僅僅關於未知
說起測試,往往與未知相關聯。我們透過試驗、除錯、檢測來獲取獲取反饋,不斷調整。
以上圖為例,一般想到的測試,都集中在“已知的未知”這個象限。正如前面的示例程式碼,使用不熟悉的庫帶來未知。程式設計師透過在測試中呼叫和觀察結果來消除未知。
然而,對於自動化測試來說,其實關注點在於已知。
“都已知了,還測試什麼呀?”,也許你會有這樣的疑問。
火柴問題
火柴,這種行將消失的物品。也許現在的小朋友只是在《賣火柴的小女孩》中才得知它的存在。在我小時候,還是時常用到的。那時,也許是工藝問題,或者儲存條件有限,往往一盒火柴好多根都不能點著。
記的那時聽到的笑話:
小明的媽媽讓他去買盒火柴,不一會功夫買回來了。媽媽問:“你試過沒有,能點著嗎?”
“試過啦”,小明很驕傲的說,“每一根我都試了一遍。”
我把這種問題稱為“火柴問題”,往往傳統的質量控制面臨的都是這類問題,有如下限制:
- 成本,顯然現實中不會有人把所有的火柴拿來測試。不過問題的本質並沒有變,在花費的成本和獲得安全保證的完全性之間取一個平衡。
- 事後,造出火柴後才有能否點著的問題,
- 因而,
- 一次性,成本換取的安全是一次性的,每當一個批次到來時,以前的測試的付出都成為了沉沒成本。
另一種測試
讓我們來看另一種關於已知的測試。
比如每天出門的時候,我都會自然而然的檢查一遍,手機、鑰匙、錢包。就是個簡單的清單。
清單是關於已知的,只有十分確定的事項才會列入在清單裡。
清單本身很簡單,並不能回答火柴問題這樣的難題。但是不代表它沒有作用。
以出門為例子,有時出門是每天都在做的上班通勤,有時是去面臨某個很大的未知,比如去見一個陌生的客戶,進行重要談判。
這時如果有個水晶球,告訴你會成功失敗,甚至告訴你怎樣做才能成功,那就太好了。
然而沒有水晶球。
一個簡單的清單至少保證你不會走在路上才發現忘帶手機。無論未知的挑戰是什麼,忘帶手機基本上不會產生任何幫助。
切換回軟體開發的場景,程式設計師夢想中的完美測試也許能告訴我們未知,甚至未知的未知結果。這在目前還不現實。那麼寫一個測試確保你在不斷調整中不破壞正確的事情,仍是值得的。
可以看到,這種視角下的驗證,與檢查火柴有所不同:
- 預防,這種校驗著眼於未來,是為了避免更大損失的投入。
- 過程中,檢查是做事步驟中的一個環節。
- 反覆,越頻繁的行為越有必要進行校驗,校驗的越頻繁潛在收益越大。
假定你是獨自居住,出門前還是鎖門後發現沒帶鑰匙的成本,會有一個巨大的飆升。往往檢查列表都是在這種成本拐點前進行的。
應對這種猛增的成本曲線有三種方式:
- 拉平曲線,透過技術改進使原本難以挽回的決定變得不那麼昂貴。
- 最佳化待檢查專案,比如現在出門帶錢包已經不那麼重要了,有手機即可。如果把門換成掃碼開鎖,那麼鑰匙也免了。這樣需要檢查的專案越少,越不容易遺漏。
- 自動化,比如遺漏了東西就有提醒警報,自然大大降低了犯錯的可能。
自測給程式設計師帶來什麼
敏捷方法論的一個基礎,就是現代軟體開發方式已經使軟體變更的成本曲線大大平緩了。我們可以看看開發者的自測在其中起到的作用。
對照上面兩個列表,可以回想一下
- 在最近的開發活動中碰到各類錯誤的比例是多少?
- 由於反饋時間和定位手段不同,解決錯誤花費的時間有何不同?
- 有多少最初百思不得其解的錯誤,長時間摸排後定位為一行修改即可改正的弱智錯誤?
- 如果這些錯都在第一時間發現,以明顯的方式報錯會怎麼樣?……
從加快反饋,幫助定位的角度思考,也許你會找到更多值得寫的測試。
記錄
常玩遊戲的同學都熟悉要時常存檔,可以讓我們安心挑戰boss,大不了失敗時返回安全點。
那麼程式碼呢?Git,SVN等程式碼管理工具使可靠的保留程式碼歷史成為可能。然而,如何在歷史中找到安全點呢?(題外話,你有嘗試過Git bisect命令麼)
記錄還帶來了另一件事,覆盤。
沒有記錄也就無從系統的進行回顧和改進。對於編碼,我們往往只能看到最終的結果。這大概也是編碼活動在軟體開發“工程——藝術” 圖譜中最偏向與藝術這一極的原因吧。
頻繁提交的程式碼歷史,加上表達行為變化的測試,會使原本大家熟視無睹的程式如實呈現出來。有興趣的話可以看看這篇cyber-dojo設計者的講演,我們甚至僅僅觀察測試變化的情況就可以對一段程式編寫的過程有個大致的瞭解。
可以透過測試改進的點
把main函式改為測試
有經驗的開發者大多都知道寫出的程式碼都至少要執行驗證一遍。然而執行程式碼有時並不那麼簡單,有的要以特定的方式部署,有的需要複雜的前置流程才能觸及。為了高效的執行程式碼,我們會採用一些手段,比如為目的碼增加一個main函式,這樣就可以直接以希望的輸入執行想要的操作,並觀察結果。
這種除錯技巧可以很容易的用測試來改寫,如下圖所示。
在基本不增加工作量的前提下,帶來如下收益:
- 明確的分離了除錯程式碼和生產邏輯。
- 避免誤導後來維護程式碼的人,也防止把測試程式碼釋出到生產環境產生隱患。
- 抹平了“除錯期——維護期”的成本差異。
- main方法的往往是在除錯階段使用。開發人員反覆調整輸入、觀察輸出、修正程式碼,直到開發完成。之後這段除錯程式就成為了過去時。後來者無法判斷這段腳手架程式碼是否還符合最新的邏輯,是否可以執行。
- 而測試程式碼在每次構建時都會自動檢查,保證程式碼保持上次變更後預期的邏輯。為開發者保留了一個除錯現場,是否“開發完了”並無顯著差異。
- 測試可以記錄多種用例
- 使用除錯方式,我們往往在確認完一個行為後修改輸入,觀察其它行為。因為預期這是一次性的工作。
- 用測試可以在不同的用例中描述行為的不同側面。方便維護者理解程式碼,也避免了,“咦,這個bug我明明測過呀”的迴歸錯誤。
- 測試明確寫出了期望的行為。
- 透過assert,測試明確的寫出可以自動判別的行為。而不是main方法中透過肉眼來閱讀理解程式行為。寫出預期會帶來如下改變:
- 幫助閱讀者理解什麼是程式碼“應該的”行為。
- 促使開發者思索程式碼的目的是什麼,會怎樣被使用。
- 自動判斷節省了開發者的注意力,更有效的反饋錯誤,定位錯誤。
用隔離依賴代替除錯程式碼
往往在除錯時,目的碼並不是純粹的邏輯處理,還會涉及到其它的外部依賴。這些依賴可能要單獨部署配置,甚至根本無法在開發環境獲得。
為了對付這種情況,一個顯而易見的方法是把目的碼copy一份到除錯程式碼處,修改依賴相關的部分。比如下圖就演示了一段程式碼,需要根據外部依賴判斷執行某操作,並更新資料庫。為了測試執行操作的邏輯,開發者copy了程式碼,註釋掉與環境相關的程式碼。
另一種類似的處理方法,在每次除錯時臨時修改目的碼,除錯結束後再恢復。
這種情況,只需要結合mock框架對外部依賴進行模擬,就可以在不改變目的碼的情況下在測試中改變程式碼行為。如上圖所示。
這種做法有避免了顯而易見的問題:
- copy程式碼方式在經歷修改後,不能保證於實際生產程式碼一致。
- 臨時修改程式碼有事後忘記恢復的風險。
除此之外,還有些潛移默化的收益:
- 使隱含的輸入輸出更加明顯了。
- 比如例子中的程式碼,從外部看起來只有一個字串輸入一個字串輸出。透過測試可以明確的看到,事實上輸入還有從外部依賴獲取的布林值,輸出還有對資料庫的操作。
- 促使程式碼向松耦合、單一職責演化。
- 有時候為了在測試中mock隔離依賴,會需要對實現程式碼稍作重構。短期看來似乎寫測試引發了更多的工作量和變更,但這種變更一般會使程式碼向職責更明確,模組間更松耦合的方向改變。促使開發者在設計層面更多的思考。
用測試來增強註釋
適當的註釋能極大的增強程式碼的可維護性。好的註釋描述程式碼在做什麼,而非怎麼做的。
對於複雜結構的處理,往往看程式碼千頭萬緒,摸不著頭腦。註釋裡附上示例資料,馬上讓人對程式碼的大致行為有所掌握。
將這種註釋中的樣例放入測試中,可以:
- 避免程式碼修改註釋無人維護的問題。
- 把不同的輸入和對應輸出一一對應起來。
利用自測促進開發
前面說了一些透過自測手段對已有工作方式的改進。事實上在熟悉掌握這些手段後,可以更進一步,主動利用測試來完成原來不能高效做到的事情。
分解“已知的未知”
對於未知的解決方案,有時是由於我們對於相關技術瞭解有限。也有一種情況,技術方面已經確定,但是由於問題較為複雜,一時看不到解決方法。
面對這種問題,一般的做法是構造式的。也就是說從自己知道的方案出發,看看需要增加什麼來接近目標,增加後調整使整體一致,再次看需要增加什麼……
還有一種分解式的方式。假定已經有了一個解決方案,從中選取一個子集,解決這個子集,然後選取下一個,直到完全解決。測試就很適合在這種方法中對問題進行分解和檢驗。
在最近的一次練習中,我就體會到即使沒有開始編碼,測試也能對解決問題起到幫助。
練習:寫一個函式,判斷兩個字串是否同構。所謂同構,是指字串A可以透過字元替換變為字串B。比如Hello 與 Apple,不同構Hello 與 Speed,同構
有興趣的同學可以自己嘗試嘗試,能否透過測試逐步分解問題找到解決方案。
提示:從最簡單確定的問題開始,比如一個字母的字串如何判斷。
顯現“未知的已知”
有多少次,當你正在開發除錯的過程中,發現了某種更好的做法。然而思索後你對自己說:“已經差不多寫好了,算了,還是以後再改吧”。即便這個改動只是給函式起個更貼切的名字而已。
而我們都知道,以後往往等於永遠也不會。
造成這種狀況的,除了我們固有的弱點,比如拖延、圖省事外,有個很重要的原因是難以評估改變的影響。還記的前面錯誤反饋列表麼?如果幾個月後才會知道有沒有問題的改動,就算再簡單我們也會避免的。這就是遺留程式碼的處境。
眾所周知,不產生bug的最佳方式就是不寫、不修改程式碼。當然這是不現實的。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69916964/viewspace-2648132/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 作為測試人員,你對自己的測試結果有信心嗎?
- 軟體測試員一定需要懂程式設計程式碼嗎?程式設計
- 測試人員對程式設計天生“恐懼”麼?程式設計
- 程式設計師與測試員的立場對立程式設計師
- 關於MySQL表設計,測試人員可以關注哪些點MySql
- 軟體測試員必須編寫程式碼嗎?掌握多少程式設計能力才夠?程式設計
- 測試工程師必須要會寫程式碼嗎?工程師
- 測試人員承接測試需求的策略
- 測試工程師必學:測試人員如何深入瞭解專案工程師
- 介面測試人員需要掌握的知識技能
- 程式設計師測試原則 - Kent Beck程式設計師
- 測試人員如何提高API功能測試效率?API
- 測試人員如何攻破物聯網測試?
- 寫給程式設計師的軟體測試指南:人人都可以開發無Bug程式碼程式設計師
- JAVA 程式設計師需要用到 10 個測試框架和庫Java程式設計師框架
- 做軟體測試需要懂程式碼嗎?
- 軟體測試人員需要具備的硬技能
- 測試人員如何上手去測試鴻蒙 NEXT鴻蒙
- 測試行業 怎麼招聘女測試人員,行業
- 遊戲測試人員如何提升自己的技術水平?遊戲
- 千鋒全棧軟體測試課程教程資源限時領,軟體測試需要懂程式設計嗎?全棧程式設計
- 自己上手寫效能測試工具(二)
- 好程式設計師Java教程分享Jmeter效能測試程式設計師JavaJMeter
- 微信小程式設計師自動化測試微信小程式程式設計師
- 軟體測試報告需要包括哪些內容?測試人員收藏這份必備指南!!!測試報告
- 測試人員的價值=自動化測試的水平?
- 老程式設計師轉測試 配置測試環境設定共享檔案 提高團隊效率程式設計師
- 測試工程師必備:掌握這5種設計方法快速編寫測試用例~思路分析工程師
- 軟體測試人員需要懂哪些常見的心理學?
- 測試人員必會SQL命令SQL
- 不寫情書,程式設計師還要學寫作嗎?程式設計師
- 程式設計師面試除了技術外,還需要注意這些程式設計師面試
- 寫給軟體測試程式設計師:這5大點必須提起注意!程式設計師
- 程式碼寫作測試
- 自己編寫的(測試點總結)
- 安卓程式設計師:如何用Espresso測試UI介面安卓程式設計師EspressoUI
- 軟體測試工程師如何提升自己工程師
- CCUT程式設計能力測試---前言程式設計