相信絕大多數從事測試行業的同志們對自動化測試有抱有一個美好的幻想,但是到底該怎麼實現和落地呢? 接下來我將結合分層測試金字塔和實際案例爲你們分享:html
項目背景: 案例項目是ThoughtWorks的內部招聘看板系統,主要服務於ThoughtWorks在19個國家49家辦公室的招聘團隊。核心功能是爲HR們提供一個展現候選人的信息的面板,生成和招聘數據有關的報表。python
提及自動化測試不得不說起測試金字塔,這種三角形的結構主要爲咱們展現了一個健康的自動化測試體系應該是什麼樣子的。如圖所示,金字塔的從上往下依次是UI測試,接口測試,單元測試, 越在高層影響就越大,花費的時間和精力就越多。圖示的測試金字塔只是一種形態示例,不一樣項目的金字塔的實現內容可能略有區別。web
在《google軟件測試之道》曾寫過: 對於google產品,70%的投入爲單元測試,20%爲集成、接口測試,10%爲UI層的自動化測試。sql
在測試金字塔中能夠看到,UI層面的自動化影響大、變化多、可維護成本高,所佔比例也最少。因此在咱們看來,UI層面的自動化應該是一些從高層次上驗證一些happy pass,確保咱們的核心功能可用。數據庫
拿案例項目來舉例,咱們識別出最核心的功能就是經過過濾器篩選數據來進行展現或計算,因此咱們的用例主要會去驗證操做這些過濾器可否篩選出正確的數據。 在技術選型上,咱們 主要用到BDD的思想,選用cucumber + capybara這套體系去作UI層的自動化。設計模式
Scenario: test office filter in China region Given I login to GoHire website When I select "China" region And I select "Chengdu" option Then I can only see all the application cards in chengdu office
UI層自動化的小經驗api
UI層自動化的適用場景是作核心功能的迴歸測試和冒煙測試,因此在實施過程當中,要注意不要把全部的用例都堆砌在UI層,而是儘量放到接口測試和單元測試中去作。app
在代碼層面,咱們能夠遵循page object的設計模式,避免在測試代碼中直接操做html元素。這樣就能夠減小重複的代碼並提升代碼的可維護性。框架
在案例項目中,接口測試主要分爲兩個部分。dom
在開發過程當中,測試人員會和開發合做去寫接口的功能測試。
在整個功能大概完成,API已經基本肯定後,測試和開發一塊兒結對寫性能測試。
接口功能測試
在寫功能測試的過程當中,咱們可能會和一些其餘的模塊或第三方API有依賴,在這種狀況下,一般能夠經過Mock的方法去解決。
如案例項目中有一個測試場景是:調用一個第三方的API,當這個API出錯時,須要接受到API返回的錯誤碼並對錯誤碼進行處理。在實現測試的時候,咱們沒有辦法讓第三方API真正的掛掉並返回錯誤碼,因此咱們只須要模擬這個請求出錯,驗證咱們代碼已經對這種錯誤進行過處理。
def should_return_error_message_when_request_failed(self): event = { "body": { "action": "update_candidate", "payload": { "candidate": { "id": 101 } } } } with requests_mock.mock() as request_mock: request_mock.get("http://api.gh.test/v1/candidates/101", text='', status_code=500) with self.assertRaises(Exception) as context: webhook.handle(event) self.assertTrue( '[request error][update_candidate] get candidate 101 from Harvest API failed' in context.exception)
接口性能測試
在功能大體完成後,咱們開始作API的性能測試。性能測試在這裏的做用主要是獲取咱們API現有的性能指標,造成一個對比的基線。在便於進行後期的優化的同時也有可能幫助咱們發現一些潛在的bug。
小故事:咱們在手工測試API的響應速度時,測試結果一切正常。當引入性能測試後就發現,這些接口在前期的響應時間確實很快,但是在請求了必定次數後會忽然變得很慢。通過調查咱們發現,這是由於咱們依賴了一個AWS的服務,這個服務有訪問頻率的限制,在最初的代碼中,每次訪問都會請求這些服務並讀取這些服務的配置,這也就致使了測試發現的問題。後來咱們更改了方案,把讀取配置這個操做放在系統初始化的時候去作,順利的解決了這個問題。
性能測試咱們主要選用了Locust這個框架。如咱們的一個接口功能是:查詢某個國家下的全部的候選人。 下面代碼用例的意思就是, 同時配置50個客戶端去訪問API,每次隨機請求某個國家的全部候選人信息,一共請求1000次,規定每次請求的時間最大不超過5s。
def get_applications_in_country(l): text = """Australia Brasil Canada China Chile Ecuador Germany India Italy Singapore Spain Turkey UK USA Uganda Thailand""" countries = text.strip().split() country = random.choice(countries) params = { "country": country, "status": "active" } with l.client.get("/getCandidates", params=params, headers={"x-api-key": API_KEY}, catch_response=True) as response: if "errorMessage" in response.text: response.failure("Error occurs in response: %s" % response.text) class UserBehavior(TaskSet): tasks = { get_applications_in_country: 1 }
function runLocustDataStoreService { validateEnvironment "HOST" "API_KEY" setupEnv CLIENTS=${CLIENTS:-"50"} HATCH_RATE=${HATCH_RATE:-"2"} NUM_REQUEST=${NUM_REQUEST:-"1000"} locust -f DataStoreService/locustfile.py --host ${HOST} --clients=${CLIENTS} --hatch-rate=${HATCH_RATE} --num-request=${NUM_REQUEST} --no-web --only-summary }
經過Locust跑完用例,咱們能夠看到在console生成的report:
圖片展現的是report的一部分,reqs表明對這個API的請求數目,fails記錄了失敗的次數,Avg、Min、Max、Mediam分別是每次請求響應時間的平均值、最小值、最大值和中位數。
單元測試是對軟件中最小的測試單元進行驗證,在這一層上發現問題時解決成本最低,測試用例的維護成本也不高,具備很好的投入產出比。通常狀況下,咱們是須要開發人員在開發過程當中寫單元測試。而做爲一個QA,咱們更多的是一個單元測試的引導者:
和團隊一塊兒制定單元測試覆蓋率的標準。
若是這是一個全新的項目,咱們能夠把覆蓋率設的相對高一點,如85%,這有利於咱們在前期就對代碼質量作出保證。若是這是一個已經相對成熟的項目,因爲前期根本沒有單元測試,咱們能夠先把要求設置的低一點,而後一步步的提高咱們的代碼覆蓋率。
爲開發人員提供單元測試的用例。
咱們須要提早把須要驗證的用例列在開發的任務卡片裏面,這樣能幫助開發更有效率的去完成咱們指望測試的用例 。
按期回顧開發人員寫的單元測試。
這裏並非要檢查代碼和具體實現,而是和開發一塊兒去回顧看看單元測試的寫法和角度是否是在同一認知上面。這樣有助於整個團隊創建一種質量保證的意識。
在具體實現上,咱們選擇nose test這個工具去作單元測試,經過nose test的插件,咱們能夠拿到單元測試覆蓋率的報表,在第二個圖中,咱們能夠看到,沒有被測試覆蓋的代碼會有紅色的標記,這樣就有利於咱們找到測試的遺漏點。
在寫單元測試時,爲了解決對數據庫的依賴,咱們能夠創建一個內存數據庫去模擬真實數據庫,便於咱們的測試用例能快速的運行。如在咱們的真實項目中,咱們的數據庫選用的是亞馬遜的RDS+Postgres,可是在作單元測試的時候咱們使用的sqlite+python綁定來模擬真實的數據庫
只讓這些自動化測試運行在本地IDE上是不夠的,在咱們的項目中,咱們創建了一套持續集成部署的體系:
推送到代碼到遠端後會自動開始運行自動化測試和代碼審查。
當單元測試經過後會自動部署到測試環境。
在部署完成後會自動生成測試報告。
小組全部成員會收到部署成功或失敗的郵件提醒。
在工具的選擇上,咱們的持續集成平臺是ThoughtWorks的GoCD,其餘相似的工具還有 jenkins,能夠靈活的選用這些工具。
在實際的項目實施過程當中,咱們實際上是按照下面的步驟依次逐步實施咱們的持續集成自動化體系的:
在項目開始以前首先搭建持續集成的框架,第一次的時候先寫一個最簡單的單元測試,如1+1=2,確保能夠在CI上運行測試,爲後續的開發奠基基礎。
開發在項目實現過程當中進行單元測試,每次開發推送代碼時均可以自動運行單元測試和代碼風格審查,當單元測試覆蓋低於85%或代碼風格檢查不經過時,構建就會失敗。
測試和開發在項目實現過程當中合做寫接口層的功能測試。
功能開發大致完成後,測試和開發合做寫接口的性能測試。
當項目發佈以後,測試開始根據核心功能編寫UI層面的自動化測試,也至關因而寫項目的迴歸測試。
最後談一點心得體會吧:
項目只有UI自動化測試是不夠的,越低層的自動化測試反而越有意義。
自動化測試的目的是減小重複的手動測試的成本,使測試人員能夠作更多有意義的事情,在實現自動化的過程當中,咱們花費的精力甚至更多。
測試並非越多越好,除了用例數量還要考慮維護代價。咱們但願測試代碼可以儘可能穩定,由於代碼須要不斷的被重構,若是發現重構一次代碼就修改不少測試,那麼這種測試可能會成爲負擔,也是一種壞味道。
測試人員在自動化測試落地的實踐中,更多的是一個推進者而不是實現者,咱們須要幫助團隊創建起一種質量保證的意識,而後共同實現自動化測試的落地。
做者簡介:雲裳,ThoughtWorks質量分析師。以dev身份加入ThoughtWorks,因興趣轉型QA。 過去一年以QA身份服務於澳洲某大型電信公司,主導項目小組的質量分析保證工做,對角色轉型和敏捷實踐有着獨特的看法。