天天看點

自動化功能測試的邏輯

持續傳遞涉及到軟體開發從需求到上線、運維全生命周期的各個活動。其中很重要的一個活動就是測試。如果沒有自動化測試,整個傳遞的節奏就會慢下來。接下來我們來聊一聊這背後的邏輯和如何才能把它做好。

軟體開發中的自動化測試可以粗略的分為自動化單元測試和自動化功能測試。二者有很多的相似之處,但同樣也有很多不同的關注點。本文主要關注的是 自動化功能測試 。

如果你的軟體是web形态的,則使用者可以通過浏覽器來使用你的軟體;如果你的軟體是手機遊戲,則使用者需要有一台手機才能使用你的軟體。而 功能測試 ,顧名思義,測的就是這些使用者能夠看到并使用的功能。

其實大多數軟體開發流程都有功能測試這個環節。比如在傳統的瀑布模式中,每次釋出之前都需要進行一輪或者多輪的回歸測試,以保證軟體功能正确。在這種模式下,手動的功能測試 就是全部的測試活動。這些測試起到了防護網的作用,保證在釋出前能夠找到大部分的bug,并修複之。聽起來不錯,那為什麼我們還是需要自動化的功能測試呢?

讓我們回到開發人員的視角來看看強哥的故事。強哥所在的團隊使用疊代開發,每個功能開發完成之後,會讓測試人員及時進行測試,每個月會進行一次釋出。

作為一個有節操的開發人員,秉承為自己的代碼負責的原則,強哥喜歡對自己所開發的功能進行細緻的分析、開發和測試。這不,手上接了一個活,經過一番分析,梳理出了8個case:

花了一個小時完成了case 1,進行了一番完備的測試。又過了一個小時,case 2也完成了。強哥把case 1和case 2都再測試一遍。如此反複,等完成case 5的時候,自然需要從1到5都測試一遍。

這時候強哥心裡有點犯嘀咕了:“case 1測了好幾遍了,都的有點想吐了,而且case 5修改的代碼其實跟case 1不是特别相關,要不先不測了,等到最後功能完成的時候在一起測試一遍吧”。于是在沒有回歸測試的前提下強哥完成了5、6、7、8這些case,而這時距離剛開始做這個功能已經過去兩天了。

“終于做完了”,強哥長籲了一口氣,“整體測一下吧”。不測不要緊,一測發現case 1、3,7都通不過了。

“還好又進行了測試”,強哥得意地想着,然後着手開始修複這些問題。經過一番艱苦卓絕的debug,終于定位了問題,并全部修複。這些工作又花掉了強哥3個小時。但強哥覺得這都是值得的,然後帶着程式員的驕傲把功能傳遞給了測試人員。強哥考慮到的這些場景經過測試人員的測試,沒有任何問題,一次通過。隻是還有兩個異常場景強哥沒有考慮到,是以又回過頭來修複了一下,這次倒是很快,10分鐘就修好了。不過為了保險起見,強哥還是把所有的場景又回歸了一遍。幸好,沒什麼問題,然後又送出給了測試人員。這次再沒有什麼問題了,通過!

就這樣,強哥帶着程式員的驕傲一次次高品質地完成了手裡的工作。然後來到了疊代末尾,距離強哥完成上面那個功能已經過去三周了。要進行釋出了,回歸測試肯定還是要做的。第一輪結束後,發現了幾個問題,其實一個竟然出現在強哥開發的功能中。“這個case我可是精心測過的,還測了好幾遍,怎麼挂了?”,強哥很不高興。其他的開發同僚跟強哥說:“别人做功能的時候,不可避免會影響已有的功能嘛,這都是正常現象,不然要回歸測試幹嘛呢?”

他說的好有道理,強哥一時竟無法反駁。在進行某一個小功能的開發時,可以在每次修改之後,都把這個功能所涵蓋的case都回歸一遍,但當這個功能送出給測試以後呢?别人在同一個代碼庫中做了某些修改,是不會對你的功能再進行一遍測試的。且不說别人都不知道你做了什麼功能,就算知道,也不可能把所有的功能都再測試一遍呀,那要花多少時間呢?

修好這個問題之後,帶着無奈和不甘心,強哥查了一下是哪次送出破壞了他的功能。讓他驚訝的是,在他完成那個功能的兩天後,也就是将近三周前,這個功能就壞掉了。強哥心裡很不舒服。。。

強哥所開發的軟體是web應用。後來強哥在一篇文章中知道了selenium可以用來做浏覽器的自動化測試。感覺豁然開朗:“如果我做的功能都能夠用這個自動化測試來覆寫,那之前的那些問題大概都能解決了吧”。

每次做完一個小功能時,補充一個自動化測試。後面每加一個功能隻需要運作一遍這個不斷增加的測試集即可。

這些測試不光可以在我開發一個功能的時候幫我去運作這些重複性的驗證,别人也可以在做完修改之後運作這些測試,以保證他沒有破壞已有的功能。

有了這些測試的保證,釋出前的回歸也會快的多。

聽起來做自動化功能測試真的是一件很美好的事情,但實際上做的人并不多。那都是什麼原因呢?

做自動化測試的技術門檻不算高,但一開始還是需要一些投入。其中包括工具的學習和使用,基礎架構的搭建、相關ci任務的搭建等。于是很多人都是這種狀态:

自動化功能測試的邏輯

測試本身也是以代碼的形式存在的,是代碼就會腐化。于是編寫測試也會變成一件成本越來越高的事情。另一方面,測試的有效性也很關鍵。比如如果一個用例聲稱會覆寫功能a,但是功能a出問題時,測試竟然沒報錯;或者當一個測試失敗10次,其中有5次是因為測試環境出錯、測試代碼本身不穩定造成的,那估計團隊也會越來越不想做寫自動化測試了。

功能測試一般都會模拟真正使用者的行為,是以相對于單元測試來說,它們都很慢。如果對軟體的所有行為編寫功能測試,随着時間的推移,你會發現跑一邊測試就需要一個小時,那還會有誰在每次送出之前運作一遍測試?

為了有效地進行自動化測試,你需要考慮很多。

要知道如何選擇工具,首先要了解自動化測試的層次:

自動化功能測試的邏輯

測試架構本身非常關鍵,一個好的測試架構本身可以幫助你編寫出可維護的測試用例,并提高編寫效率。你可以使用junit、rspec之類的工具可以達到目的。但cucumber和robotframe等工具可以提供更多更好的功能,詳情可以參看筆者的另一篇文章:cucumber和robotframework對比。如果你在進行基于rack的應用程式的測試,那capybara絕對是不二的選擇。

一個好的測試驅動同樣可以大幅提升用例開發的效率。在對網絡通信的測試中,如果沒有scapy這個庫的支援,恐怕編寫用例的成本要增加一個數量級。在web測試的上下文中,不光要考慮測試庫是否能夠滿足你的功能性需求,還要考慮運作速度。基于真實浏覽器的測試是很慢的,是以出現了phontamjs這樣的headless driver。

資料準備同樣是編寫測試中一個非常重要的步驟。你可以選擇自己往資料庫裡面插資料,并進行清理,但如果有一個工具可以幫助你做類似的事情,豈不是很好?ruby世界的factorygirl,和databasecleaner可以在這方面提供很多幫助,節省你大量的時間。

能找到合适的工具是一件很幸運的事情。很多時候也可能找不到很好的工具。但這也沒關系,就像編寫産品代碼一樣,自己花時間寫一個相應的庫或者工具就好。

環境不穩定、測試代碼本身不穩定等因素都會導緻大家越來越不想去運作測試。是以要在第一時間修複這些問題。舉兩個例子:

我工作過的一個軟體系統會依賴與一個搜尋引擎。由于搜尋引擎是收費軟體,為了節省成本,持續內建所使用的搜尋引擎和手工測試使用的搜尋引擎是同一個執行個體。是以有時候手工測試在其中注入資料時會導緻自動化測試失敗。後來又花錢買了一份liscense部署了一台新的執行個體,測試穩定性有了很大的提升。

如果發現了不穩定的測試,要第一時間修複。如果一時半會修複不了怎麼辦呢?我所在的團隊曾經采用過的一種方法是打一個@quarantine的标簽給這個測試,然後修改運作腳本,跳過所有打了這個标簽的測試。然後再慢慢修複這個測試。這樣就可以保證整個測試集是健康的,測試結果一定能夠給你帶來有效的資訊。

如果我告訴你為了運作整個測試集,你需要手動重建資料庫,然後feed一些測試資料到資料庫中,然後啟動應用程式a、b和c,然後再配置一系列的環境變量,然後再運作一串巨長的帶着各種參數的指令,你是否還願意跑這個測試?為何不把這些繁瑣的工作寫到一個腳本中,讓别人簡單地運作一個短短的指令就可以完成這個工作?這個腳本可以是一個shell腳本,但如果能夠內建到項目所使用的建構工具中就更好了。在ruby項目中可以內建到rake中,在node項目中可以內建到gulp中,在java項目中可以內建到gradle中。

好了,現在你可以友善地運作所有的測試了。但是别忘了開發人員大部分的時間是在編寫和調試單個用例,是以請保證你選用的測試架構或者ide能夠支援單個用例的運作和調試。

自動化功能測試的運作速度是比較慢的。即使你已經采用了類似phantomjs這樣的驅動,所有測試的整體運作時間還是不可避免的會很長。在持續內建伺服器上可以采用并行的方式讓多台機器來同時運作測試。但在本地如何并行運作呢?答案是挑選其中一部分我們覺得非常重要的測試來運作,而不是全部。具體的做法就是在我們認為很重要的用例上打上@prepush之類的标簽,然後修改啟動測試的腳本來隻運作打了這些标簽的測試。經驗值是運作prepush的時間不要超過5分鐘。

有了這個prepush腳本,就可以把它嵌在git push的hook中(假設你使用的是git),以保證每個人在送出代碼的時候都能夠運作一遍這些測試,進而確定自己的修改沒有破壞别人的功能。

當然了,自動化測試也不是萬能的。舉個例子,強哥對自己能夠想到的功能進行了完備的測試,但是有些異常場景如果沒有考慮到,自然也就沒法編寫相應的測試。是以需求分析和用例設計其實是一個單獨的、但也同等重要的課題。

上文提到了很多次功能測試運作時間太久的問題,這個問題除了使用prepush和并行運作之外還有什麼辦法可以解決嗎?答案就是自動化單元測試和測試金字塔!當然本文就不對這個話題展開讨論。

自動化功能測試本身并不是目的,并不是你跟别人說:“看,我的測試覆寫率超過90%”,你的項目就成功了。代碼沒bug、開發可以快速傳遞、市場認可才是最終目的,而自動化功能測試隻是實作這些的目标的一個手段而已。即不是唯一的手段,也不是必不可少的手段。