在Django下測試與除錯REST API的方法詳解
對於大多數研發人員來說,都期望能找到一個良好的測試/除錯方法,來提高工作效率和快速解決問題。所謂除錯,偏重於對某個bug的查詢、定位、修復;所謂測試,是檢驗某個功能是否達到預期效果。測試發現問題後進行除錯,從而解決問題。
對於後臺研發來說,往往沒有客戶端研發(Windows/Android等等)那樣簡單有效的DEBUG方法,比如Step by Step。雖然目前有很多IDE可以實現本地除錯,但是因為後臺研發的環境複雜,你很難在一臺機器上模擬所有的環境,比如線上的資料庫只能在內網訪問等等,所以很多時候需要在伺服器(DEV/STG/PROD)上進行除錯和測試工作。本文嘗試介紹一些Django Web服務下除錯/測試REST API的方法,因為是方法,那自然仁者見仁智者見智,所以也許你覺得會有用,也許會覺得毫無道理。
首先要說的是,在伺服器環境,針對Django Web服務,print log是最簡單有效的方法。python的語言特性決定了你可以隨時新增log,重新run一下就可以看到結果。本文接下來要說的,並不是什麼超脫於pring log的超級技巧,而是如何編寫程式碼、利用工具進行有效的除錯/測試工作,並儘量做到減少重複性工作。
對於任何一個REST API來說,需要進行測試的內容包括兩部分:請求中的各個功能模組是否正常、整個請求是否正常。對於研發人員來說,寫完程式碼後當然要進行一番測試來確認程式碼是否正常工作、是否符合需求。這時,有人選擇先測試一下整個請求是否正常,如果正常就萬事大吉,交付給測試同事測試了,如果不正常則進一步檢視是哪個功能模組出現問題;有人選擇先逐一測試各個功能模組是否正常,在確保各功能模組正常後,再進一步測試整個請求是否正常,然後交付測試。這兩種行事風格並沒有特別的利弊之說,最好是在不同的情形下選擇使用,比如對於簡單的功能API,可以使用第一種,而對於複雜的功能API,選擇第二種。不管行事風格如何,都會涉及都前面所述的兩部分測試內容,即分與合的兩部分測試。Django下,常見的有這麼兩種方法,或者說目前在我們專案組使用最廣泛的兩種方法:
1. 對於功能模組,使用python manage.py shell來進行除錯與測試。該命令幫助我們啟動一個python 的shell環境,並自動載入了Django的上下文資訊,我們可以在裡面import任何函式和類,構建引數,除錯/測試其相關的功能。如果需要定位問題,一種方法是在原始檔中加入更多的log,一種方法是將該功能模組逐行敲一遍,看看在哪邊出現了問題。
2. 對於整個請求,使用工具curl(linux下)或者Advanced REST Client(Windows下,Chome的一個外掛)來構建一個Request,傳送給後臺服務。筆者使用更多的是第二個,其可以構建各個Method的Request,並且可以儲存相應的請求,還可以分享你儲存的列表給別人使用。傳送請求後,通過後臺log來檢視請求是否正常及相關的異常錯誤問題,你可以使用tail -f /var/log/test.log來檢視動態log資訊。
在合適的位置增加規範化的log對於後臺服務來說,是一件非常重要的事情,能幫助我們在線上環境及時發現問題,log的書寫應該包括相應的Tag,ErrorCode等等資訊,方便查詢。
上述兩個方法,可以很好的對Django下的Web服務進行除錯/測試,但是卻有著一些缺憾。對應第一種方法,通過python manage.py shell進行功能測試,意味著每次都需要啟動一個shell,每次都需要將相應的程式碼敲一遍,存在著重複性的工作,其實這種方法更適用於應對特殊的CASE,而不是針對Common的功能進行除錯/測試。對於第二種方法,首先,當你想增加一些log時,你需要重啟一下服務才能生效,而這種情況在生產環境就比較棘手;其次,通過這種方法進行測試,你沒有辦法在程式中處理一些Response的結果。
Python和Django自帶了一些測試框架和工具,而充分利用這些,正好可以解決上述兩種方法的缺憾。常用的一些測試框架和工具有:
1)單元測試框架TestCase;
2)Django的Test Client,類似curl的工具,是一個類似內建瀏覽器的工具,可以發起一個請求,並拿到對應的response內容;
3)RequestFactory,允許構建一個request引數,實現象呼叫函式一樣呼叫一個view的請求函式,可以繞過middleware。
結合前面兩種方法的缺憾和上述的測試框架/工具,下面介紹兩種筆者常用的兩種除錯/測試方法:
1. 對於功能模組,採用單元測試的方法。單元測試常見於TDD的研發流程中,一方面可以驗證所寫的程式模組執行後的行為是否符合設計的測試用例,另一方面可以在修改程式碼後,快速驗證是否改出了問題,是否與原有行為保持一致。採用單元測試的方法,既可以實現功能模組的除錯/測試功能,也能有效的避免重複性工作,有效的彌補了前面所述的第一種方法的缺點。
python本身自帶了一套unittest框架,而diango對於它又做了一層封裝,可以根據喜好選擇使用,筆者通常還是習慣使用python自帶的單元測試框架。一個簡單的單元測試程式碼如下:
import unittest class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(),'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(),['hello','world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) if __name__ == '__main__': unittest.main()
2. 對於整個請求,採用RequestFactory來實現類函式式呼叫的方法,從而可以實現呼叫view的請求函式。採用這個方法,可以有效避免前面第二種方法中需要重啟服務的缺陷,當你修改了程式碼後,只要重新reload一下模組,即可實現新的呼叫;同時,你也可以在程式碼中拿到對應的response內容,做必要的校驗處理。
然而,對於我們使用rest framework 框架來做REST API開發來說,Django原生的RequestFactory還是有一點缺陷,就是不能做authenticate。因為rest framework中的authenticate是做在request前面的,而不是在middleware中,所以採用原生的RequestFactory無法繞過authenticate。不過,rest framework又重新封裝了一個APIRequestFactory類,提供了相關的模擬鑑權功能,常見的用法如下:
from rest_framework.test import APIRequestFactory from rest_framework.test import force_authenticate # Using the standard RequestFactory API to create a form POST request factory = APIRequestFactory() request = factory.post('/notes/',{'title': 'new idea'},format='json') # force authenticate user = User.objects.get(username='olivia') force_authenticate(request,user=user) # call the view request view = AccountDetail.as_view() response = view(request)
上面所述的方法,只是筆者目前常用的四種方法,在實際工作中根據需要進行選擇使用。當然,我相信還有更多更好的其他方法。
這篇在Django下測試與除錯REST API的方法詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。