Python接口自動化測試的實現
1)環境準備:
接口測試的方式有很多,比如可以用工具(jmeter,postman)之類,也可以自己寫代碼進行接口測試,工具的使用相對來說都比較簡單,重點是要搞清楚項目接口的協議是什么,然后有針對性的進行選擇,甚至當工具不太適合項目時需要自己進行開發。
在我們項目的初期,我們采用的是jmeter進行接口測試,當時覺得這個工具上手簡單,團隊成員學習成本低,并且接口測試的腳本稍微調整一下還可以用來做性能測試。
不過隨著項目規模、團隊人數的不斷增長,漸漸的這個工具有適應不了當前項目的需求了,為此我們項目也重新開發了相關接口自動化的平臺。但是,但是。。。可能是我讓大家中毒太深,現在很多同學一提到接口測試關聯到jmeter,為此,我深深感到不安。畢竟jmeter只是個工具,換個項目換個協議你是否還能玩轉接口測試呢?session和cookie有什么區別?工具又是怎么實現的呢?
比如session如何保存,接口依賴如何處理,case如何管理及執行順序,測試數據如何管理等等題,這個過程也有助于我們更加深刻的理解接口測試和http協議。
本文主要采用python語言,python中http協議接口相關的庫有urllib,urllib2以及reqeusts庫,這其中reqeusts庫用來起來最方便,因此我也主要采用requests庫來做http協議的接口測試。首先來看下需要哪些環境信息:
一、安裝python
mac下自帶安裝了python,這個不多說了。
二、安裝虛擬環境:
我們在一臺機器上可以安裝多個python版本,為了使每個版本的環境相互不受干擾,可以安裝虛擬環境,安裝方法如下:
1、安裝virtualenv:pip install virtualenv
2、新建名為venv的虛擬環境:virtualenv venv
3、進入新環境:source venv/bin/activate
4、退出:deactivate
三、安裝requests庫:
>>>pip install requests
ps:用python做http協議的接口測試會用到這個庫。
四、http測試工具:
一個使用 Python + Flask 編寫的 HTTP 請求和響應服務,該服務主要用于測試 HTTP 庫。后續測試我們都基于這個網站。
http://httpbin.org
五、在本地搭建httpbin:
考慮到測試時要不斷訪問 httpbin 網站,請求過多擔心被拉到黑名單,我們自己在本志搭建一套httpbin服務。
1、安裝:pip install gunicorn
2、安裝:pip install httpbin
3、啟動:gunicorn httpbin:app
至此,環境搭建已經完畢,可以開始玩了~
(2)requests.get()
環境搭建好后,接下來我們先來了解一下requests的一些簡單使用,主要包括:
requests常用請求方法使用,包括:get,postrequests庫中的Session、Cookie的使用其它高級部分:認證、代理、證書驗證、超時配置、錯誤異常處理等。
本節首先來了解一下requests庫中如何發送get請求:
一、看下方法定義:
1、到官方文檔去了下requests.get()方法的定義,如下:
2、點擊右上角的【source】,看一下它的源碼如下:
看到最后一行return,get方法最后是通過調用 requests.request方法實現的,其實在其它的請求方法如post,put,head,delete等方法都是調用的request方法,然后把請求方法的類型傳遞給request方法第一個參數。
3、HTTP協議是一個基于請求/響應模式的、無狀態的,應用層協議。既然有請求,就有響應,來看下resquest中常用的響應信息:
二、get方法簡單使用:
1、不帶參數的get:
# -*- coding:utf-8 -*-#不帶參數的getimport requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'get'url = ’’.join([host,endpoint])r = requests.get(url)#response = r.json()print type(r.text)print (eval(r.text))
輸出:
{ ’origin’: ’183.14.133.88’, ’headers’: { ’Connection’: ’close’, ’Host’: ’httpbin.org’, ’Accept-Encoding’: ’gzip, deflate’, ’Accept’: ’*/*’, ’User-Agent’: ’python-requests/2.18.1’ }, ’args’: { }, ’url’: ’http: //httpbin.org/get’}
2、 帶參數的get:
# -*- coding:utf-8 -*-#帶參數的getimport requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'get'url = ’’.join([host,endpoint])params = {'show_env':'1'}r = requests.get(url=url,params=params)print r.url
輸出:
http://httpbin.org/get?show_env=1{ ’origin’: ’183.14.133.88’, ’headers’: { ’X-Request-Id’: ’ebe922b4-c463-4fe9-9faf-49748d682fd7’, ’Accept-Encoding’: ’gzip, deflate’, ’X-Forwarded-Port’: ’80’, ’Total-Route-Time’: ’0’, ’Connection’: ’close’, ’Connect-Time’: ’0’, ’Via’: ’1.1vegur’, ’X-Forwarded-For’: ’183.14.133.88’, ’Accept’: ’*/*’, ’User-Agent’: ’python-requests/2.18.1’, ’X-Request-Start’: ’1504755961007’, ’Host’: ’httpbin.org’, ’X-Forwarded-Proto’: ’http’ }, ’args’: { ’show_env’: ’1’ }, ’url’: ’http: //httpbin.org/get?show_env=1’}
3、帶header的get:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'get'url = ’’.join([host,endpoint])headers = {'User-Agent':'test request headers'}r = requests.get(url)r = requests.get(url,headers=headers)#response = r.json()print (eval(r.text))[’headers’][’User-Agent’]
輸出:
test request headers
4、同時帶參數和header:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'get'url = ’’.join([host,endpoint])headers = {'User-Agent':'test request headers'}params = {'show_env':'1'}r = requests.get(url)r = requests.get(url,headers=headers,params=params)#response = r.json()print (eval(r.text))[’headers’][’User-Agent’]print r.url
輸出:
test request headershttp://httpbin.org/get?show_env=1
(3)requests.post()
一、方法定義
二、post方法簡單使用
1、帶數據的post
2、帶header的post
3、帶json的post
4、帶參數的post
5、普通文件上傳
6、定制化文件上傳
7、多文件上傳
一、方法定義:
1、到官方文檔去了下requests.post()方法的定義,如下:
2、源碼:
3、常用返回信息:
二、post方法簡單使用:
1、帶數據的post:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])data = {’key1’:’value1’,’key2’:’value2’}r = requests.post(url,data=data)#response = r.json()print (r.text)
輸出:
{ 'args': {}, 'data': '', 'files': {}, 'form': { 'key1': 'value1', 'key2': 'value2' }, 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '23', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.18.1' }, 'json': null, 'origin': '183.14.133.88', 'url': 'http://httpbin.org/post'}
2、帶header的post:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])headers = {'User-Agent':'test request headers'}# r = requests.post(url)r = requests.post(url,headers=headers)#response = r.json()
輸出:
{ 'args': {}, 'data': '', 'files': {}, 'form': {}, 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '0', 'Host': 'httpbin.org', 'User-Agent': 'test request headers' }, 'json': null, 'origin': '183.14.133.88', 'url': 'http://httpbin.org/post'}
3、帶json的post:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])data = { 'sites': [{ 'name':'test' , 'url':'www.test.com' },{ 'name':'google' , 'url':'www.google.com' },{ 'name':'weibo' , 'url':'www.weibo.com' } ]}r = requests.post(url,json=data)# r = requests.post(url,data=json.dumps(data))response = r.json()
輸出:
{ 'args': {}, 'data': '{'sites': [{'url': 'www.test.com', 'name': 'test'}, {'url': 'www.google.com', 'name': 'google'}, {'url': 'www.weibo.com', 'name': 'weibo'}]}', 'files': {}, 'form': {}, 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '140', 'Content-Type': 'application/json', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.18.1' }, 'json': { 'sites': [ { 'name': 'test', 'url': 'www.test.com' }, { 'name': 'google', 'url': 'www.google.com' }, { 'name': 'weibo', 'url': 'www.weibo.com' } ] }, 'origin': '183.14.133.88', 'url': 'http://httpbin.org/post'}
4、帶參數的post:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])params = {’key1’:’params1’,’key2’:’params2’}# r = requests.post(url)r = requests.post(url,params=params)#response = r.json()print (r.text)
輸出:
{'args': {'key1': 'params1','key2': 'params2'},'data': '','files': {},'form': {},'headers': {'Accept': '*/*','Accept-Encoding': 'gzip, deflate','Connection': 'close','Content-Length': '0','Host': 'httpbin.org','User-Agent': 'python-requests/2.18.1'},'json': null,'origin': '183.14.133.88','url': 'http://httpbin.org/post?key2=params2&key1=params1'}
5.普通文件上傳:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])#普通上傳files = { ’file’:open(’test.txt’,’rb’) }r = requests.post(url,files=files)print (r.text)
輸出:
{ 'args': {}, 'data': '', 'files': { 'file': 'hello world!n' }, 'form': {}, 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '157', 'Content-Type': 'multipart/form-data; boundary=392865f79bf6431f8a53c9d56c62571e', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.18.1' }, 'json': null, 'origin': '183.14.133.88', 'url': 'http://httpbin.org/post'}
6.定制化文件上傳:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])#自定義文件名,文件類型、請求頭files = { ’file’:(’test.png’,open(’test.png’,’rb’),’image/png’)}r = requests.post(url,files=files)print (r.text)heman793
7.多文件上傳:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])#多文件上傳files = [ (’file1’,(’test.txt’,open(’test.txt’, ’rb’))), (’file2’, (’test.png’, open(’test.png’, ’rb’))) ]r = requests.post(url,files=files)print (r.text)
8.流式上傳:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'post'url = ’’.join([host,endpoint])#流式上傳with open( ’test.txt’ ) as f: r = requests.post(url,data = f)print (r.text)
輸出:
{ 'args': {}, 'data': 'hello world!n', 'files': {}, 'form': {}, 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '13', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.18.1' }, 'json': null, 'origin': '183.14.133.88', 'url': 'http://httpbin.org/post'}
(4)Cookie&Session
掌握了前面幾節的的內容,就可以做一些簡單的http協議接口的請求發送了,但是這些還不夠。HTTP協議是一個無狀態的應用層協議,也就是說前后兩次請求是沒有任何關系的,那如果我們測試的接口之前有相互依賴關系怎么辦呢(比如我要在博客園發文章,是需要先登錄的),這時我們就要用到cookie和session技術來保持客戶端與服務器端連接的狀態,這也就是本節要介紹的內容:
一、Cookie:
1、獲取cookie:
# -*- coding:utf-8 -*-#獲取cookieimport requestsimport jsonurl = 'https://www.baidu.com/'r = requests.get(url)#將RequestsCookieJar轉換成字典c = requests.utils.dict_from_cookiejar(r.cookies)print r.cookiesprint cfor a in r.cookies: print a.name,a.value
輸出:
<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>{’BDORZ’: ’27315’}BDORZ 27315
2、發送Cookie
# -*- coding:utf-8 -*-#發送cookie到服務器import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'cookies'url = ’’.join([host,endpoint])#方法一:簡單發送# cookies = {'aaa':'bbb'}# r = requests.get(url,cookies=cookies)# print r.text#方法二:復雜發送s = requests.session()c = requests.cookies.RequestsCookieJar()c.set(’c-name’,’c-value’,path=’/xxx/uuu’,domain=’.test.com’)s.cookies.update(c)
二、Session
1、保持會話同步:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'cookies'url = ’’.join([host,endpoint])url1 = 'http://httpbin.org/cookies/set/sessioncookie/123456789'r = requests.get(url)print r.textprint '------'s = requests.session() #初始化一個session對象s.get(url1)#cookie的信息存在了session中r = s.get(url)print r.text
輸出:
{ 'cookies': {}}------{ 'cookies': { 'sessioncookie': '123456789' }}
2、保存繪畫信息:
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'headers'url = ’’.join([host,endpoint])header1 = {'testA':'AAA'}header2 = {'testB':'BBB'}s = requests.session() #初始化一個session對象s.headers.update(header1) #已經存在于服務中的信息r = s.get(url,headers=header2) #發送新的信息print r.text
輸出:
{ 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'Testa': 'AAA', 'Testb': 'BBB', 'User-Agent': 'python-requests/2.18.1' }}
3.刪除已存在的會話信息,保存為None
# -*- coding:utf-8 -*-import requestsimport jsonhost = 'http://httpbin.org/'endpoint = 'headers'url = ’’.join([host,endpoint])header1 = {'testA':'AAA'}header2 = {'testB':'BBB'}s = requests.session() #初始化一個session對象s.headers.update(header1) #已經存在于服務中的信息r = s.get(url,headers=header2) #發送新的信息print r.textprint ’--------’s.headers[’testA’] = None #刪除會話里的信息testAr1 = s.get(url,headers = header2)print r1.text
{ 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'Testa': 'AAA', 'Testb': 'BBB', 'User-Agent': 'python-requests/2.18.1' }}--------{ 'headers': { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'Testb': 'BBB', 'User-Agent': 'python-requests/2.18.1' }}
4、提供默認數據:
s = requests.Session()s.auth = (’user’, ’pass’)s.headers.update({’x-test’: ’true’})# both ’x-test’ and ’x-test2’ are sents.get(’http://httpbin.org/headers’, headers={’x-test2’: ’true’})
參考:
http://docs.python-requests.org/en/master/user/quickstart/#cookieshttp://docs.python-requests.org/en/master/user/advanced/#session-objects
(5)其他(認證&代理&超時設置)
一、認證
1、基本認證:
# -*- coding:utf-8 -*-import requestsurl = 'http://httpbin.org/basic-auth/user/passwd'r1 = requests.get(url)print '未提供用戶名密碼:' + str(r1.status_code)#Basic Authenticationr2 = requests.get(url,auth=(’user’,’passwd’))print '已提供用戶名密碼:' + str(r2.status_code)
輸出:
未提供用戶名密碼:401已提供用戶名密碼:200
2、數字認證:
>>> from requests.auth import HTTPDigestAuth>>> url = ’http://httpbin.org/digest-auth/auth/user/pass’>>> requests.get(url, auth=HTTPDigestAuth(’user’, ’pass’))<Response [200]>
3、OAuth認證:
參考:http://docs.python-requests.org/en/master/user/authentication/
二、代理
1、方法一:proxy參數:
import requestsproxies = { 'https': 'http://41.118.132.69:4433'}r = requests.post('http://httpbin.org/post', proxies=proxies)print r.text
2、方法二:設置環境變量:
$ export HTTP_PROXY='http://10.10.1.10:3128'$ export HTTPS_PROXY='http://10.10.1.10:1080'$ python>>> import requests>>> requests.get(’http://example.org’)
3、HTTP Basic Auth使用代理方法:http://user:password@host/
proxies = {’http’: ’http://user:pass@10.10.1.10:3128/’}
三、證書驗證
1、SSL證書(HTTPS):
import requests#跳過12306 的證書驗證,把 verify 設置為 False: r = requests.get(’https://kyfw.12306.cn/otn/’, verify=False)print r.text
2、客戶端證書:
>>> requests.get(’https://kennethreitz.org’, cert=(’/path/client.cert’, ’/path/client.key’))<Response [200]>
or
s = requests.Session()s.cert = ’/path/client.cert’
四、超時配置
1 、利用timeout參數來配置最大請求時間:
r = requests.get(’https://github.com’, timeout=5)
2、設置timeout=None,告訴請求永遠等待響應,而不將請求作為超時值傳遞
r = requests.get(’https://github.com’, timeout=None)
五、錯誤異常:
1、所有Requests顯式拋出的異常都繼承自:requests.exctptions.RequestException
2、遇到網絡問題(如:DNS查詢失敗,拒絕連接等)時,requests會拋出一個ConnectionError異常
3、遇到罕見的無效HTTP響應時,Request則會拋出一個HTTPError異常
4、若請求超時,則拋出一個Timeout異常
5、若請求超過了最大的重寫向次數,則會拋出一個TooManyRedirects異常
(6)unittest-單個用例管理:
上面主要介紹了環境搭建和requests庫的使用,可以使用這些進行接口請求的發送。但是如何管理接口案例?返回結果如何自動校驗?這些內容光靠上面五節是不行的,因此從本節開始我們引入python單元測試框架 unittest,用它來處理批量用例管理,校驗返回結果,初始化工作以及測試完成后的環境復原工作等等。
一、單個用例管理起來比較簡單,參考如下圖,單個用例一般多用在調試的時候:
二、代碼如下:
# -*- coding:utf-8 -*-# 單個用例執行# 1、導入模塊import unittest# 2、繼承自unittest.TestCase類class TestOne(unittest.TestCase): # 3、配置環境:進行測試前的初始化工作 def setUp(self): print ’ncases before’ pass # 4、定義測試用例,名字以“test”開頭 def test_add(self): ’’’test add method’’’ print ’add...’ a = 3 + 4 b = 7 # 5、定義assert斷言,判斷測試結果 self.assertEqual(a, b) def test_sub(self): ’’’test sub method’’’ print ’sub...’ a = 10 - 5 b = 4 self.assertEqual(a, b) # 6、清理環境 def tearDown(self): print ’case after’ pass# 7、該方法會搜索該模塊下所有以test開頭的測試用例方法,并自動執行它們if __name__ == ’__main__’: unittest.main()
輸出:
Ran 2 tests in 0.001sOKcases beforeadd...case aftercases beforesub...case afterProcess finished with exit code 0
(8)unittest-生成測試報告:
用例的管理問題解決了后,接下來要考慮的就是報告我問題了,這里生成測試報告主要用到HTMLTestRunner.py 這個模塊,下面簡單介紹一下如何使用:
一、下載HTMLTestRunner下載:
這個模塊不能通過pip安裝,只能下載安裝,下載地址如下:
python2.x版本:http://tungwaiyip.info/software/HTMLTestRunner.html
python3.x版本:http://hzqldjb.blog.51cto.com/9587820/1590802
二、mac下配置:
1、終端進入python環境
2、輸入:
import sysprint sys.path
3、找到site-packages文件夾的路徑并將下載的HTMLTestRunner.py文件拷貝到此的文件夾下
4、在python環境下,輸入 import HTMLTestRunner 不報錯即安裝成功
三、使用該模塊生成報告:
1、目錄結構
case包下面有baidu,httpbin兩個包 每個包下面分別有兩個測試的py文件 每個test_00x.py文件里各有2個test case run_all_case.py文件:用來執行所有的test case且生成測試報告2、運行后生成報告如下:
3、run_all_case.py代碼如下:
# -*- coding:utf-8 -*-import unittestimport osimport timeimport HTMLTestRunner# 用例路徑case_path = os.path.join(os.getcwd())# 報告存放路徑report_path = os.path.join(os.getcwd(), ’report’)print report_pathdef all_case(): discover = unittest.defaultTestLoader.discover(case_path, pattern='test*.py', top_level_dir=None) print discover return discoverif __name__ == ’__main__’: # 1、獲取當前時間,這樣便于下面的使用。 now = time.strftime('%Y-%m-%d-%H_%M_%S', time.localtime(time.time())) # 2、html報告文件路徑 report_abspath = os.path.join(report_path, 'result_'+now+'.html') # 3、打開一個文件,將result寫入此file中 fp = open(report_abspath, 'wb') runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u’接口自動化測試報告,測試結果如下:’, description=u’用例執行情況:’) # 4、調用add_case函數返回值 runner.run(all_case()) fp.close()
到此這篇關于Python接口自動化測試的實現的文章就介紹到這了,更多相關Python接口自動化測試內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
相關文章:
1. 每日六道java新手入門面試題,通往自由的道路--多線程2. ASP.NET MVC視圖頁使用jQuery傳遞異步數據的幾種方式詳解3. SpringBoot+SpringCache實現兩級緩存(Redis+Caffeine)4. php讀取xml中某個元素的內容(PHP5以上才支持)5. ASP.NET泛型三之使用協變和逆變實現類型轉換6. Idea 2019.3 本應該搜索到的插件卻搜索不到的解決方法7. 在JSP中使用formatNumber控制要顯示的小數位數方法8. 快速解決ajax返回值給外部函數的問題9. 每日六道java新手入門面試題,通往自由的道路10. IntelliJ IDEA導入jar包的方法
