日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Python單元測(cè)試的九個(gè)技巧

本文轉(zhuǎn)載自微信公眾號(hào)「游戲不存在」,作者肖恩 。轉(zhuǎn)載本文請(qǐng)聯(lián)系游戲不存在公眾號(hào)。

pytest是python的單元測(cè)試框架,簡(jiǎn)單易用,在很多知名項(xiàng)目中應(yīng)用。requests是python知名的http爬蟲庫,同樣簡(jiǎn)單易用,是python開源項(xiàng)目的TOP10。關(guān)于這2個(gè)項(xiàng)目,之前都有過介紹,本文主要介紹requests項(xiàng)目如何使用pytest進(jìn)行單元測(cè)試,希望達(dá)到下面3個(gè)目標(biāo):

  1. 熟練pytest的使用
  2. 學(xué)習(xí)如何對(duì)項(xiàng)目進(jìn)行單元測(cè)試
  3. 深入requests的一些實(shí)現(xiàn)細(xì)節(jié)

本文分如下幾個(gè)部分:

  • requests項(xiàng)目單元測(cè)試狀況
  • 簡(jiǎn)單工具類如何測(cè)試
  • request-api如何測(cè)試
  • 底層API測(cè)試

requests項(xiàng)目單元測(cè)試狀況

requests的單元測(cè)試代碼全部在 tests 目錄,使用 pytest.ini 進(jìn)行配置。測(cè)試除pytest外,還需要安裝:

庫名描述
httpbin一個(gè)使用flask實(shí)現(xiàn)的http服務(wù),可以客戶端定義http響應(yīng),主要用于測(cè)試http協(xié)議
pytest-httpbinpytest的插件,封裝httpbin的實(shí)現(xiàn)
pytest-mockpytest的插件,提供mock
pytest-covpytest的插件,提供覆蓋率

上述依賴 master 版本在requirement-dev文件中定義;2.24.0版本會(huì)在pipenv中定義。

測(cè)試用例使用make命令,子命令在Makefile中定義, 使用make ci運(yùn)行所有單元測(cè)試結(jié)果如下:

 
 
 
  1. $ make ci 
  2. pytest tests --junitxml=report.xml 
  3. ======================================================================================================= test session starts ======================================================================================================= 
  4. platform linux -- Python 3.6.8, pytest-3.10.1, py-1.10.0, pluggy-0.13.1 
  5. rootdir: /home/work6/project/requests, inifile: pytest.ini 
  6. plugins: mock-2.0.0, httpbin-1.0.0, cov-2.9.0 
  7. collected 552 items                                                                                                                                                                                                                
  8.  
  9. tests/test_help.py ...                                                                                                                                                                                                      [  0%] 
  10. tests/test_hooks.py ...                                                                                                                                                                                                     [  1%] 
  11. tests/test_lowlevel.py ...............                                                                                                                                                                                      [  3%] 
  12. tests/test_packages.py ...                                                                                                                                                                                                  [  4%] 
  13. tests/test_requests.py .................................................................................................................................................................................................... [ 39%] 
  14. 127.0.0.1 - - [10/Aug/2021 08:41:53] "GET /stream/4 HTTP/1.1" 200 756 
  15. .127.0.0.1 - - [10/Aug/2021 08:41:53] "GET /stream/4 HTTP/1.1" 500 59 
  16. ---------------------------------------- 
  17. Exception happened during processing of request from ('127.0.0.1', 46048) 
  18. Traceback (most recent call last): 
  19.   File "/usr/lib64/python3.6/wsgiref/handlers.py", line 138, in run 
  20.     self.finish_response() 
  21. x.........................................................................................                                                                                                                                 [ 56%] 
  22. tests/test_structures.py ....................                                                                                                                                                                               [ 59%] 
  23. tests/test_testserver.py ......s....                                                                                                                                                                                        [ 61%] 
  24. tests/test_utils.py ..s................................................................................................................................................................................................ssss [ 98%] 
  25. ssssss.....                                                                                                                                                                                                                 [100%] 
  26.  
  27. ----------------------------------------------------------------------------------- generated xml file: /home/work6/project/requests/report.xml ----------------------------------------------------------------------------------- 
  28. ======================================================================================= 539 passed, 12 skipped, 1 xfailed in 64.16 seconds ======================================================================================== 

可以看到requests在1分鐘內(nèi),總共通過了539個(gè)測(cè)試用例,效果還是不錯(cuò)。使用 make coverage 查看單元測(cè)試覆蓋率:

 
 
 
  1. $ make coverage 
  2. ----------- coverage: platform linux, python 3.6.8-final-0 ----------- 
  3. Name                          Stmts   Miss  Cover 
  4. ------------------------------------------------- 
  5. requests/__init__.py             71     71     0% 
  6. requests/__version__.py          10     10     0% 
  7. requests/_internal_utils.py      16      5    69% 
  8. requests/adapters.py            222     67    70% 
  9. requests/api.py                  20     13    35% 
  10. requests/auth.py                174     54    69% 
  11. requests/certs.py                 4      4     0% 
  12. requests/compat.py               47     47     0% 
  13. requests/cookies.py             238    115    52% 
  14. requests/exceptions.py           35     29    17% 
  15. requests/help.py                 63     19    70% 
  16. requests/hooks.py                15      4    73% 
  17. requests/models.py              455    119    74% 
  18. requests/packages.py             16     16     0% 
  19. requests/sessions.py            283     67    76% 
  20. requests/status_codes.py         15     15     0% 
  21. requests/structures.py           40     19    52% 
  22. requests/utils.py               465    170    63% 
  23. ------------------------------------------------- 
  24. TOTAL                          2189    844    61% 
  25. Coverage XML written to file coverage.xml 

結(jié)果顯示requests項(xiàng)目總體覆蓋率61%,每個(gè)模塊的覆蓋率也清晰可見。

單元測(cè)試覆蓋率使用代碼行數(shù)進(jìn)行判斷,Stmts顯示模塊的有效行數(shù),Miss顯示未執(zhí)行到的行。如果生成html的報(bào)告,還可以定位到具體未覆蓋到的行;pycharm的coverage也有類似功能。

tests下的文件及測(cè)試類如下表:

文件描述
compatpython2和python3兼容
conftestpytest配置
test_help,test_packages,test_hooks,test_structures簡(jiǎn)單測(cè)試類
utils.py工具函數(shù)
test_utils測(cè)試工具函數(shù)
test_requests測(cè)試requests
testserver\server模擬服務(wù)
test_testserver模擬服務(wù)測(cè)試
test_lowlevel使用模擬服務(wù)測(cè)試模擬網(wǎng)絡(luò)測(cè)試

簡(jiǎn)單工具類如何測(cè)試

test_help 實(shí)現(xiàn)分析

先從最簡(jiǎn)單的test_help上手,測(cè)試類和被測(cè)試對(duì)象命名是對(duì)應(yīng)的。先看看被測(cè)試的模塊help.py。這個(gè)模塊主要是2個(gè)函數(shù) info 和 _implementation:

 
 
 
  1. import idna 
  2.  
  3. def _implementation(): 
  4.     ... 
  5.      
  6. def info(): 
  7.     ... 
  8.     system_ssl = ssl.OPENSSL_VERSION_NUMBER 
  9.     system_ssl_info = { 
  10.         'version': '%x' % system_ssl if system_ssl is not None else '' 
  11.     } 
  12.     idna_info = { 
  13.         'version': getattr(idna, '__version__', ''), 
  14.     } 
  15.     ... 
  16.     return { 
  17.         'platform': platform_info, 
  18.         'implementation': implementation_info, 
  19.         'system_ssl': system_ssl_info, 
  20.         'using_pyopenssl': pyopenssl is not None, 
  21.         'pyOpenSSL': pyopenssl_info, 
  22.         'urllib3': urllib3_info, 
  23.         'chardet': chardet_info, 
  24.         'cryptography': cryptography_info, 
  25.         'idna': idna_info, 
  26.         'requests': { 
  27.             'version': requests_version, 
  28.         }, 
  29.     } 

info提供系統(tǒng)環(huán)境的信息,_implementation是其內(nèi)部實(shí)現(xiàn),以下劃線*_*開頭。再看測(cè)試類test_help:

 
 
 
  1. from requests.help import info 
  2.  
  3. def test_system_ssl(): 
  4.     """Verify we're actually setting system_ssl when it should be available.""" 
  5.     assert info()['system_ssl']['version'] != '' 
  6.  
  7. class VersionedPackage(object): 
  8.     def __init__(self, version): 
  9.         self.__version__ = version 
  10.  
  11. def test_idna_without_version_attribute(mocker): 
  12.     """Older versions of IDNA don't provide a __version__ attribute, verify 
  13.     that if we have such a package, we don't blow up. 
  14.     """ 
  15.     mocker.patch('requests.help.idna', new=None) 
  16.     assert info()['idna'] == {'version': ''} 
  17.  
  18. def test_idna_with_version_attribute(mocker): 
  19.     """Verify we're actually setting idna version when it should be available.""" 
  20.     mocker.patch('requests.help.idna', new=VersionedPackage('2.6')) 
  21.     assert info()['idna'] == {'version': '2.6'} 

首先從頭部的導(dǎo)入信息可以看到,僅僅對(duì)info函數(shù)進(jìn)行測(cè)試,這個(gè)容易理解。info測(cè)試通過,自然覆蓋到_implementation這個(gè)內(nèi)部函數(shù)。這里可以得到單元測(cè)試的第1個(gè)技巧:

1.僅對(duì)public的接口進(jìn)行測(cè)試

test_idna_without_version_attribute和test_idna_with_version_attribute均有一個(gè)mocker參數(shù),這是pytest-mock提供的功能,會(huì)自動(dòng)注入一個(gè)mock實(shí)現(xiàn)。使用這個(gè)mock對(duì)idna模塊進(jìn)行模擬

 
 
 
  1. # 模擬空實(shí)現(xiàn) 
  2. mocker.patch('requests.help.idna', new=None) 
  3. # 模擬版本2.6 
  4. mocker.patch('requests.help.idna', new=VersionedPackage('2.6')) 

可能大家會(huì)比較奇怪,這里patch模擬的是 requests.help.idna , 而我們?cè)趆elp中導(dǎo)入的是 inda 模塊。這是因?yàn)樵趓equests.packages中對(duì)inda進(jìn)行了模塊名重定向:

 
 
 
  1. for package in ('urllib3', 'idna', 'chardet'): 
  2.     locals()[package] = __import__(package) 
  3.     # This traversal is apparently necessary such that the identities are 
  4.     # preserved (requests.packages.urllib3.* is urllib3.*) 
  5.     for mod in list(sys.modules): 
  6.         if mod == package or mod.startswith(package + '.'): 
  7.             sys.modules['requests.packages.' + mod] = sys.modules[mod] 

使用mocker后,idna的__version__信息就可以進(jìn)行控制,這樣info中的idna結(jié)果也就可以預(yù)期。那么可以得到第2個(gè)技巧:

2.使用mock輔助單元測(cè)試

test_hooks 實(shí)現(xiàn)分析

我們繼續(xù)查看hooks如何進(jìn)行測(cè)試:

 
 
 
  1. from requests import hooks 
  2.  
  3. def hook(value): 
  4.     return value[1:] 
  5.  
  6. @pytest.mark.parametrize( 
  7.     'hooks_list, result', ( 
  8.         (hook, 'ata'), 
  9.         ([hook, lambda x: None, hook], 'ta'), 
  10.     ) 
  11. def test_hooks(hooks_list, result): 
  12.     assert hooks.dispatch_hook('response', {'response': hooks_list}, 'Data') == result 
  13.  
  14. def test_default_hooks(): 
  15.     assert hooks.default_hooks() == {'response': []} 

hooks模塊的2個(gè)接口default_hooks和dispatch_hook都進(jìn)行了測(cè)試。其中default_hooks是純函數(shù),無參數(shù)有返回值,這種函數(shù)最容易測(cè)試,僅僅檢查返回值是否符合預(yù)期即可。dispatch_hook會(huì)復(fù)雜一些,還涉及對(duì)回調(diào)函數(shù)(hook函數(shù))的調(diào)用:

 
 
 
  1. def dispatch_hook(key, hooks, hook_data, **kwargs): 
  2.     """Dispatches a hook dictionary on a given piece of data.""" 
  3.     hooks = hooks or {} 
  4.     hooks = hooks.get(key) 
  5.     if hooks: 
  6.         # 判斷鉤子函數(shù) 
  7.         if hasattr(hooks, '__call__'): 
  8.             hooks = [hooks] 
  9.         for hook in hooks: 
  10.             _hook_data = hook(hook_data, **kwargs) 
  11.             if _hook_data is not None: 
  12.                 hook_data = _hook_data 
  13.     return hook_data 

pytest.mark.parametrize提供了2組參數(shù)進(jìn)行測(cè)試。第一組參數(shù)hook和ata很簡(jiǎn)單,hook是一個(gè)函數(shù),會(huì)對(duì)參數(shù)裁剪,去掉首位,ata是期望的返回值。test_hooks的response的參數(shù)是Data,所以結(jié)果應(yīng)該是ata。第二組參數(shù)中的第一個(gè)參數(shù)會(huì)復(fù)雜一些,變成了一個(gè)數(shù)組,首位還是hook函數(shù),中間使用一個(gè)匿名函數(shù),匿名函數(shù)沒有返回值,這樣覆蓋到 if _hook_data is not None: 的旁路分支。執(zhí)行過程如下:

  • hook函數(shù)裁剪Data首位,剩余ata
  • 匿名函數(shù)不對(duì)結(jié)果修改,剩余ata
  • hook函數(shù)繼續(xù)裁剪ata首位,剩余ta

經(jīng)過測(cè)試可以發(fā)現(xiàn)dispatch_hook的設(shè)計(jì)十分巧妙,使用pipeline模式,將所有的鉤子串起來,這是和事件機(jī)制不一樣的地方。細(xì)心的話,我們可以發(fā)現(xiàn) if hooks: 并未進(jìn)行旁路測(cè)試,這個(gè)不夠嚴(yán)謹(jǐn),有違我們的第3個(gè)技巧:

3.測(cè)試盡可能覆蓋目標(biāo)函數(shù)的所有分支

test_structures 實(shí)現(xiàn)分析

LookupDict的測(cè)試用例如下:

 
 
 
  1. class TestLookupDict: 
  2.  
  3.     @pytest.fixture(autouse=True) 
  4.     def setup(self): 
  5.         """LookupDict instance with "bad_gateway" attribute.""" 
  6.         self.lookup_dict = LookupDict('test') 
  7.         self.lookup_dict.bad_gateway = 502 
  8.  
  9.     def test_repr(self): 
  10.         assert repr(self.lookup_dict) == "
  11.  
  12.     get_item_parameters = pytest.mark.parametrize( 
  13.         'key, value', ( 
  14.             ('bad_gateway', 502), 
  15.             ('not_a_key', None) 
  16.         ) 
  17.     ) 
  18.  
  19.     @get_item_parameters 
  20.     def test_getitem(self, key, value): 
  21.         assert self.lookup_dict[key] == value 
  22.  
  23.     @get_item_parameters 
  24.     def test_get(self, key, value): 
  25.         assert self.lookup_dict.get(key) == value 

可以發(fā)現(xiàn)使用setup方法配合@pytest.fixture,給所有測(cè)試用例初始化了一個(gè)lookup_dict對(duì)象;同時(shí)pytest.mark.parametrize可以在不同的測(cè)試用例之間復(fù)用的,我們可以得到第4個(gè)技巧:

4.使用pytest.fixture復(fù)用被測(cè)試對(duì)象,使用pytest.mark.parametriz復(fù)用測(cè)試參數(shù)

通過TestLookupDict的test_getitem和test_get可以更直觀的了解LookupDict的get和__getitem__方法的作用:

 
 
 
  1. class LookupDict(dict): 
  2.     ... 
  3.     def __getitem__(self, key): 
  4.         # We allow fall-through here, so values default to None 
  5.         return self.__dict__.get(key, None) 
  6.  
  7.     def get(self, key, default=None): 
  8.         return self.__dict__.get(key, default) 
  • get自定義字典,使其可以使用 get 方法獲取值
  • __getitem__自定義字典,使其可以使用 [] 符合獲取值

CaseInsensitiveDict的測(cè)試用例在test_structures和test_requests中都有測(cè)試,前者主要是基礎(chǔ)測(cè)試,后者偏向業(yè)務(wù)使用層面,我們可以看到這兩種差異:

 
 
 
  1. class TestCaseInsensitiveDict: 
  2.     # 類測(cè)試 
  3.     def test_repr(self): 
  4.         assert repr(self.case_insensitive_dict) == "{'Accept': 'application/json'}" 
  5.  
  6.     def test_copy(self): 
  7.         copy = self.case_insensitive_dict.copy() 
  8.         assert copy is not self.case_insensitive_dict 
  9.         assert copy == self.case_insensitive_dict 
  10.  
  11. class TestCaseInsensitiveDict: 
  12.     # 使用方法測(cè)試 
  13.     def test_delitem(self): 
  14.         cid = CaseInsensitiveDict() 
  15.         cid['Spam'] = 'someval' 
  16.         del cid['sPam'] 
  17.         assert 'spam' not in cid 
  18.         assert len(cid) == 0 
  19.  
  20.     def test_contains(self): 
  21.         cid = CaseInsensitiveDict() 
  22.         cid['Spam'] = 'someval' 
  23.         assert 'Spam' in cid 
  24.         assert 'spam' in cid 
  25.         assert 'SPAM' in cid 
  26.         assert 'sPam' in cid 
  27.         assert 'notspam' not in cid 

借鑒上面的測(cè)試方法,不難得出第5個(gè)技巧:

5.可以從不同的層面對(duì)同一個(gè)對(duì)象進(jìn)行單元測(cè)試

后面的test_lowlevel和test_requests也應(yīng)用了這種技巧

utils.py

utils中構(gòu)建了一個(gè)可以寫入env的生成器(由yield關(guān)鍵字提供),可以當(dāng)上下文裝飾器使用:

 
 
 
  1. import contextlib 
  2. import os 
  3.  
  4. @contextlib.contextmanager 
  5. def override_environ(**kwargs): 
  6.     save_env = dict(os.environ) 
  7.     for key, value in kwargs.items(): 
  8.         if value is None: 
  9.             del os.environ[key] 
  10.         else: 
  11.             os.environ[key] = value 
  12.     try: 
  13.         yield 
  14.     finally: 
  15.         os.environ.clear() 
  16.         os.environ.update(save_env) 

下面是使用方法示例:

 
 
 
  1. # test_requests.py 
  2.  
  3. kwargs = { 
  4.     var: proxy 
  5. # 模擬控制proxy環(huán)境變量 
  6. with override_environ(**kwargs): 
  7.     proxies = session.rebuild_proxies(prep, {}) 
  8.      
  9. def rebuild_proxies(self, prepared_request, proxies):   
  10.     bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) 
  11.   
  12. def should_bypass_proxies(url, no_proxy): 
  13.     ... 
  14.     get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper()) 
  15.     ... 

6.涉及環(huán)境變量的地方,可以使用上下文裝飾器進(jìn)行模擬多種環(huán)境變量

utils測(cè)試用例

utils的測(cè)試用例較多,我們選擇部分進(jìn)行分析。先看to_key_val_list函數(shù):

 
 
 
  1. # 對(duì)象轉(zhuǎn)列表 
  2. def to_key_val_list(value): 
  3.     if value is None: 
  4.         return None 
  5.  
  6.     if isinstance(value, (str, bytes, bool, int)): 
  7.         raise ValueError('cannot encode objects that are not 2-tuples') 
  8.  
  9.     if isinstance(value, Mapping): 
  10.         value = value.items() 
  11.  
  12.     return list(value) 

對(duì)應(yīng)的測(cè)試用例TestToKeyValList:

 
 
 
  1. class TestToKeyValList: 
  2.  
  3.     @pytest.mark.parametrize( 
  4.         'value, expected', ( 
  5.             ([('key', 'val')], [('key', 'val')]), 
  6.             ((('key', 'val'), ), [('key', 'val')]), 
  7.             ({'key': 'val'}, [('key', 'val')]), 
  8.             (None, None) 
  9.         )) 
  10.     def test_valid(self, value, expected): 
  11.         assert to_key_val_list(value) == expected 
  12.  
  13.     def test_invalid(self): 
  14.         with pytest.raises(ValueError): 
  15.             to_key_val_list('string') 

重點(diǎn)是test_invalid中使用pytest.raise對(duì)異常的處理:

7.使用pytest.raises對(duì)異常進(jìn)行捕獲處理

TestSuperLen介紹了幾種進(jìn)行IO模擬測(cè)試的方法:

 
 
 
  1. class TestSuperLen: 
  2.  
  3.     @pytest.mark.parametrize( 
  4.         'stream, value', ( 
  5.             (StringIO.StringIO, 'Test'), 
  6.             (BytesIO, b'Test'), 
  7.             pytest.param(cStringIO, 'Test', 
  8.                          marks=pytest.mark.skipif('cStringIO is None')), 
  9.         )) 
  10.     def test_io_streams(self, stream, value): 
  11.         """Ensures that we properly deal with different kinds of IO streams.""" 
  12.         assert super_len(stream()) == 0 
  13.         assert super_len(stream(value)) == 4 
  14.  
  15.     def test_super_len_correctly_calculates_len_of_partially_read_file(self): 
  16.         """Ensure that we handle partially consumed file like objects.""" 
  17.         s = StringIO.StringIO() 
  18.         s.write('foobarbogus') 
  19.         assert super_len(s) == 0 
  20.      
  21.     @pytest.mark.parametrize( 
  22.         'mode, warnings_num', ( 
  23.             ('r', 1), 
  24.             ('rb', 0), 
  25.         )) 
  26.     def test_file(self, tmpdir, mode, warnings_num, recwarn): 
  27.         file_obj = tmpdir.join('test.txt') 
  28.         file_obj.write('Test') 
  29.         with file_obj.open(mode) as fd: 
  30.             assert super_len(fd) == 4 
  31.         assert len(recwarn) == warnings_num 
  32.  
  33.     def test_super_len_with_tell(self): 
  34.         foo = StringIO.StringIO('12345') 
  35.         assert super_len(foo) == 5 
  36.         foo.read(2) 
  37.         assert super_len(foo) == 3 
  38.  
  39.     def test_super_len_with_fileno(self): 
  40.         with open(__file__, 'rb') as f: 
  41.             length = super_len(f) 
  42.             file_data = f.read() 
  43.         assert length == len(file_data) 
  • 使用StringIO來模擬IO操作,可以配置各種IO的測(cè)試。當(dāng)然也可以使用BytesIO/cStringIO, 不過單元測(cè)試用例一般不關(guān)注性能,StringIO簡(jiǎn)單夠用。
  • pytest提供tmpdir的fixture,可以進(jìn)行文件讀寫操作測(cè)試
  • 可以使用__file__來進(jìn)行文件的只讀測(cè)試,__file__表示當(dāng)前文件,不會(huì)產(chǎn)生副作用。

8.使用IO模擬配合進(jìn)行單元測(cè)試

request-api如何測(cè)試

requests的測(cè)試需要httpbin和pytest-httpbin,前者會(huì)啟動(dòng)一個(gè)本地服務(wù),后者會(huì)安裝一個(gè)pytest插件,測(cè)試用例中可以得到httpbin的fixture,用來操作這個(gè)服務(wù)的URL。

功能
TestRequestsrequests業(yè)務(wù)測(cè)試
TestCaseInsensitiveDict大小寫不敏感的字典測(cè)試
TestMorselToCookieExpirescookie過期測(cè)試
TestMorselToCookieMaxAgecookie大小
TestTimeout響應(yīng)超時(shí)的測(cè)試
TestPreparingURLsURL預(yù)處理
...一些零碎的測(cè)試用例

坦率的講:這個(gè)測(cè)試用例內(nèi)容龐大,達(dá)到2500行。看起來是針對(duì)各種業(yè)務(wù)的零散case,我并沒有完全理順其組織邏輯。我選擇一些感興趣的業(yè)務(wù)進(jìn)行介紹, 先看TimeOut的測(cè)試:

 
 
 
  1. TARPIT = 'http://10.255.255.1' 
  2.  
  3. class TestTimeout: 
  4.  
  5.     def test_stream_timeout(self, httpbin): 
  6.         try: 
  7.             requests.get(httpbin('delay/10'), timeout=2.0) 
  8.         except requests.exceptions.Timeout as e: 
  9.             assert 'Read timed out' in e.args[0].args[0] 
  10.      
  11.     @pytest.mark.parametrize( 
  12.     'timeout', ( 
  13.         (0.1, None), 
  14.         Urllib3Timeout(connect=0.1, read=None) 
  15.     )) 
  16.     def test_connect_timeout(self, timeout): 
  17.         try: 
  18.             requests.get(TARPIT, timeout=timeout) 
  19.             pytest.fail('The connect() request should time out.') 
  20.         except ConnectTimeout as e: 
  21.             assert isinstance(e, ConnectionError) 
  22.             assert isinstance(e, Timeout) 

test_stream_timeout利用httpbin創(chuàng)建了一個(gè)延遲10s響應(yīng)的接口,然后請(qǐng)求本身設(shè)置成2s,這樣可以收到一個(gè)本地timeout的錯(cuò)誤。test_connect_timeout則是訪問一個(gè)不存在的服務(wù),捕獲連接超時(shí)的錯(cuò)誤。

TestRequests都是對(duì)requests的業(yè)務(wù)進(jìn)程測(cè)試,可以看到至少是2種:

 
 
 
  1. class TestRequests: 
  2.      
  3.     def test_basic_building(self): 
  4.      &nbs
    網(wǎng)站名稱:Python單元測(cè)試的九個(gè)技巧
    網(wǎng)頁URL:http://www.5511xx.com/article/cceieie.html