前言
- 文章比較詳細,建議先收藏
- 下一步:專案的具體測驗
- 有任何問題,評論區留言
單元測驗目錄
- 單元測驗的目的
- 單元測驗的由來
- 單元測驗的優勢
- Request模塊
- Request模塊簡介
- 介紹
- 安裝
- 請求方式以及回應
- HTTP介面動詞說明
- 查詢引數對應的request模塊中的請求方式
- Request模塊使用(Httpbin.org)
- Django單元測驗
- 說明
- 資料庫環境準備
- Testcase
- setUp和tearDown函式
- assert斷言捕獲例外的方法
- Client
- 說明
- 登錄測驗兩種實作方法
- FactoryRequest
- 簡介
- 用戶中心登錄測驗
- mock
- 簡介
- mock實操
- Pytestest單元測驗
- Pytest介紹
- pytest是什么
- pytest的優點
- Pytest環境準備
- 安裝
- 組態檔
- demo
- 運行方式
- Pytest相關函式使用
- Pytest-assert+warning
- Pytest-setup/teardown
- Pytest-fixture
- Pytest-mark
- Pytest的插件
- Pytest-html-生成測驗報告
- Pytest-orderingrun-控制函式執行順序
- Pytest-rerunfailures-重測驗失敗的用例
- Yaml
- Yaml是什么
- PyYAML決議
單元測驗的目的
單元測驗的由來
在軟體測驗的發展程序中衍生的一種測驗方法,能夠更高效率、更簡潔地實作軟體系統的模塊化的測驗,
單元測驗的優勢
- 極大程度上節約測驗時間
復雜的互動場景中自動化地幫助自己測驗組件,更快速的定位錯誤代碼位置, - 測驗本身不僅是發現錯誤,更能預防錯誤
測驗能夠協助開發以更清晰的思路認識自己的代碼問題,只有認識到代碼問題才能夠讓代碼優化,帶給客戶更優質的體驗, - 能夠讓代碼更加簡潔
測驗后的代碼,邏輯更加清晰,能夠減少代碼冗余,
Request模塊
Request模塊簡介
介紹
一、說明
? Request模塊是一個模擬用戶在客戶端發送各種請求的基于HTTP協議的Python庫
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-P8V9rGXa-1602593196197)(C:\Users\Pine\Desktop\03.png)]
二、作用
- 軟體測驗使用:腳本化發送請求并分析回傳資料是否例外
- 爬蟲使用:模擬用戶腳本化自動采集網路資源
安裝
# 切換虛擬環境!!! 安裝鏡像源為清華園
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
請求方式以及回應
HTTP介面動詞說明

查詢引數對應的request模塊中的請求方式
| 查詢引數 | 所有請求方式->requests.get(params={}) |
|---|---|
| 請求體 json引數 | 非get請求方式(post、put、delete)->requests.post(json={}) |
| 請求體 form引數 | 非get請求方式(post、put、delete)->requests.post(data={}) |
| 引數傳遞方式 | requests 接收的關鍵引數名字 |
| 請求頭引數 | 所有請求方式->requests.get(headers={}) |
Request模塊使用(Httpbin.org)
httpbin.org說明:之后的測驗案例都采用對這個網站進行請求的方案
- 作用:測驗HTTP的Request請求并回傳對應的Response資訊,適用于GET、POST各種請求介面
- 專案架構:基于Python的Flask框架 開源
- Github地址:https://github.com/Runscope/httpbin
- 官網:http://httpbin.org/
-
Get 請求
import requests from requests import Request, Session # 1. 定義URL物件 # url = 'http://www.meiduo.site:8080/' url = 'http://httpbin.org/get' # 2. 新建request物件 注意+請求方式 response = requests.get(url) # 3. 檢查獲取的資料格式以及內容列印到一個檔案中 with open('test1.html', 'w') as f: f.write(response.text) # 4. 可以列印response觀察物件型別 print(type(response)) # text方法默認列印的回傳資料的json格式 print(response1.text) # 默認列印的是回傳資料二進制資料 print(response1.content) # b'{\n "args": {}, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Host": "httpbin.org", \n "User-Agent": "python-requests/2.23.0", \n "X-Amzn-Trace-Id": "Root=1-5f818280-636d615a13bf7f173724951b"\n }, \n "origin": "106.121.5.147", \n "url": "http://httpbin.org/get"\n}\n'- 回傳結果展示

- 回傳結果展示
-
POST請求
# 1. URL URL = 'http://httpbin.org/post' # 2. request物件 json_dict = { 'user': '吳亦凡' } # 傳入資料 data:表單資料 json:json資料 # response = requests.post(URL, data=json_dict) # response = requests.get(url,params=json_dict) response = requests.post(URL, json=json_dict) # 3. 寫入檔案 with open('test1.html', 'w', encoding='utf-8') as f: f.write(response.text)- 回傳結果

- 回傳結果
-
回應物件相關屬性說明
# 1.獲取回應物件的資料 text(requests 會使用其推測的文本編碼)->string型別 response.text # 2.獲取回應物件的資料 content->bytes型別 # decode() 將 bytes-> str response.content.decode() # 3.前端回傳狀態碼(無論資料是否驗證正確,都會回傳200狀態碼,測驗判斷不能用狀態碼) response.status_code # 4.前后端分離開發模式下,存在一個問題無論資料是否驗證正確,都會回傳200狀態碼,測驗判斷不能用狀態碼 # 測驗網站登錄功能,介面規定登錄成功回傳json->{'code':0 ,'msg':oK} # 4.1獲取回應物件的 json資料---json() response.json() # 5.獲取請求頭資訊 response.requests.headers # 6.獲取回應頭資訊 response.headers # =====防止被反爬添加請求頭的資訊==== # 添加請求頭資訊 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36' } request.get(url, headers=headers) -
Cookie請求
cookie作用:1. 常用與用戶登錄后,登錄狀態資訊的保存 2. 快取一些其他資訊
# ===方法1:request物件中攜帶cookies==== # 1. url url = 'http://httpbin.org/cookies' # 2. 定義cookie值 dict:強制型別轉換 cookies = dict(cokie_is='Richerd') # 3. response加上cookie值 # response = requests.post(URL, data=cookies) response = requests.get(url, cookies=cookies) # print(response.text) # 4. 寫入檔案 with open('test1.html', 'w', encoding='utf-8') as f: f.write(response.text) # ===方法2: 直接使用cookie中的RequestsCookieJar()=== # 這里引入 request.cookie # 1.實體化cookie_jar物件 jar = requests.cookies.RequestsCookieJar() # 2.設定cookie # jar.set(請求頭key值, value值, 域名, 路徑) jar.set('coo', 'zhenxiang', domain='httpbin.org', path='/cookies') # 3.url url = 'http://httpbin.org/cookies' # 4.獲取物件 response = requests.get(url, cookies=jar) print(response.text) -
Session請求
seesion作用:和cookie類似用于保存一些資訊,但瀏覽器中往往存的是seesionid值,真正的session資訊存盤在資料庫中
request模塊中Session和Cookie的請求登錄的區分
- request.get()
requests.get() 就可以發送請求, 但是 遇到涉及登錄的時候, 應該先發送登錄請求->登錄成功獲取cookies->可以去請求需要登錄認證的頁面
* 1.login請求: 攜帶賬戶和密碼 response = requests.post(url, json=xxx) * 2.決議登錄成功的:cookie = response.request._cookies * 3.登錄用戶中心請求 '/mytaobao...':requests.get('<https://i.taobao.com/my_taobao.htm?spm=xxxx', cookies=cookies)- session.get()
- 1.先訪問的是login的請求-- 攜帶賬戶和密碼 response = session.post()
- 2.再發送 info 請求 – session.get(‘info’)
from requests import Session # 1.創建session物件 s = Session() # 2.發送帶cookie的請求 用url設定一個cookie值 之后的cookie將會一直和session物件系結 s.get('http://httpbin.org/cookies/set/sessioncookie/123456789') # 3.獲取cookie值 檢查是否獲取成功 r = s.get("http://httpbin.org/cookies") print(r.text) # 1. 設定 用戶名和密碼 即設定session物件的auth屬性默認值 s.auth = ('user', 'pass') # 2. 可以直接增加請求頭中資訊(在原有請求頭欄位中添加) s.headers.update({'test1': 'wangyibo'}) # 3. 請求頭中再次以引數形式添加另一個請求頭資訊 并檢查請求頭中是否已經添加成功 # 注意這里或默認auth之前添加的資訊也會一塊合并到請求頭中 r = s.get('http://httpbin.org/headers', headers={'test2': 'yibowangzha'}) print(r.text)- 回傳結果

Django單元測驗
注意!:Django每次測驗每次測驗之前會自動創建一個和專案默認資料庫名稱相同的default臨時資料庫,而且臨時資料庫是復制了專案所有的表但是沒有資料,測驗用例測驗結束后,會把臨時創建的default資料庫洗掉
說明
- Django的單元測驗模塊繼承自Python自帶的測驗基類unittest
- postman和unitest的區別
- Postman適用于介面測驗 —>黑盒測驗
- unitest適用于代碼邏輯測驗 —>白盒測驗
資料庫環境準備
在Django的settings組態檔中進行對測驗資料庫配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'mysql',
'NAME': 'mysql',
'OPTION': {'charset': 'utf8'},
# 配置測驗的資料庫
# 1.單元測驗需要使用的資料庫,測驗執行時,會自動創建新的default資料庫包含TEST庫
# 2.每次自動創建的資料庫中沒有測驗資料,而且測驗執行完會自動洗掉測驗資料庫
# 3.如果和default配置相同,使用默認配置
'TEST':{
'NAME':'test',
'CHARSET': 'utf8', # 設定測驗資料庫的字符集
'COLLATION': 'utf8_general_ci', # 測驗資料表中的行的編碼
},
},
}
Testcase
注意事項!!!
- 由于使用的是Django自帶的測驗模塊,所以所有的測驗代碼都必須在專案的目錄下,且為Python的package
- 創建的測驗函式函式名組成格式 test_xxx
- 執行方式為終端執行 python manage.py test 默認一鍵執行所有的測驗代碼
- 執行摸個應用的測驗 python manage.py 應用名
- 執行應用下的某個測驗檔案 python manage.py 應用名.test_xxx
- 執行應用下測驗檔案中的具體測驗類 python manage.py 應用名.test_xxx.Demotest
- 具體到函式 執行應用下測驗檔案中的具體測驗類 python manage.py 應用名.test_xxx.Demotest.test_xxx
setUp和tearDown函式
-
setUp tearDown
setUp 和 tearDown函式作用就是在測驗用例代碼執行前/后 做一些準備動作 !!!每個測驗用例執行前后都會執行
使用場景:測驗之前打開檔案 測驗之后關閉檔案
class TestDemo(TestCase): def setUp(self): print('---setup---') def tearDown(self) -> None: print('---tearDown---\n') def test_two(self): print('---222---') def test_three(self): print('---3333---') def test_four(self): print('---4444---') class TestDemo2(TestCase): def test_two(self): print('\n---TestDemo---')測驗結果:
---setup--- ---4444--- ---tearDown--- ---setup--- ---3333--- ---tearDown--- ---setup--- ---222--- ---tearDown--- ---TestDemo--- -
@classmthod修飾
setUpclass 和 tearDownclass 函式在這個類中的所有測驗用例執行前后只會執行一次
使用場景:測驗用例測驗之前資料庫中添加測驗資料
class ThreeTest(TestCase): @classmethod def setUpClass(cls): print('setUpClass--每個測驗用例類---呼叫一次') @classmethod def tearDownClass(cls): print('tearDownClass---每個測驗用例類---呼叫一次') def test_baidu(self): url = 'http://www.baidu.com' response = requests.get(url) print(response.status_code) def test_tencent(self): url = 'http://www.tencent.com' response = requests.get(url) print(response.status_code)回傳結果:
setUpClass--每個測驗用例類---呼叫一次 200 200 tearDownClass---每個測驗用例類---呼叫一次 -
登錄測驗
class LoginTest(TestCase): def test_login(self): url = 'http://www.heiheihei.site:8000/login/' json_dict = {'username': 'wangyibo', 'password': '12345678'} response = requests.post(url, json=json_dict) print(response.content.decode()) # 登錄成功回傳json字串 {"code": 0, "errmsg": "ok"}
assert斷言捕獲例外的方法
# assert 是一個python的關鍵字 作用是捕獲例外 詳細說明查看 https://blog.csdn.net/fengtian12345/article/details/80529510
# 正常使用assert函式
assert (1+1==2)
assert (1+1==3) # 條件為 false時觸發 AssertionError 例外
# 測驗中使用到的 assert語法 self
self.assert(用于判斷的變數,判斷值,'如果不符合條件的提示')
Django的TestCase中封裝的assert方法:

Client
說明
-
client 自帶login方法 和session類似可以在client物件在被銷毀之前一直攜帶登錄的cookie
-
client的login不用url地址
-
client和session發送請求的區別
session發送的真請求 client發送的偽請求(專案不用運行,直接測驗代碼)
登錄測驗兩種實作方法
unicode-escape編碼集 長這樣
b``'\\u4e2d\\u56fd'
# ===================方法1:不使用login方法==================
class LoginTestCase(TestCase):
def setUp(self):
# 準備資料庫資料 加一個用戶
User.objects.create_user(username='liudehua', password='11111111')
self.client = Client()
def test_login(self):
# 1.介面url
url = '/login/'
# 2.設定登錄用戶
json_dict = {'username': 'liudehua', 'password': '11111111'}
# 3.用client物件進行 post登錄訪問測驗 添加content_type='application/json' 確保向后臺回傳json字串資料
response = self.client.post(url, data=json_dict, content_type='application/json')
# 4.檢查回傳資料 是否登陸成功
print(response.content.decode())
# 5.由于回傳json資料可能含有其他資料 我們這里只取后臺回傳的狀態碼code
# 為斷言判斷取值
code = json.loads(response.content.decode()).get('code')
# 6.斷言函式 如果code為0則登錄成功 否則輸出‘登錄失敗'
self.assertEqual(code, 0, '登錄失敗')
# =================方法2:使用login函式====================
class LoginTestCase(TestCase):
def setUp(self):
# 準備資料庫資料 加一個用戶
User.objects.create_user(username='wangyibo', password='11111111')
self.client = Client()
def test_login(self):
# 1.使用login函式直接發送請求
login_result = self.client.login(request='post', username='wangyibo', password='11111111')
# 登錄成功回傳True 失敗False
print(login_result)
# 2.使用斷言函式直接判斷是否登錄成功
self.assertEqual(login_result, True, '登錄失敗')
# 3.登錄成功訪問個人中心(檢查cookie攜帶)
response = self.client.get('/info/')
# 4.再使用斷言判斷是否訪問成功
code = json.loads(response.content.decode())['code']
print(response.content.decode())
# 檢查cookie資訊
print('登錄成功之后的cookie:', self.client.cookies)
# 判斷
self.assertEqual(code, 0, '個人中心訪問失敗!')
def tearDown(self) -> None:
# 退出登錄
self.client.logout()
# 檢查退出登錄之后的cookie值是否還在
print('退出之后的cookie', self.client.cookies)
FactoryRequest
簡介
-
注意事項
- 只接受get post put delete head options 和trace 方法,這些方法接受的引數大部分相同, 因為它們只是用來生成請求
- 它不支持中間件,會話和身份驗證屬性必須由測驗本身提供(如果需要)才能使視圖正常作業
-
作用
模擬一個請求物件,直接可以把請求物件傳給指定的視圖函式(作為引數),不需要手動指定url地址,
-
原理
# 類視圖:類名稱.as_view()(請求物件) # 視圖函式:視圖函式(請求物件) python manage.py shell # shell中匯入 from django.http import HttpRequest from apps.users.views import LoginView # 發送請求 登錄介面實作 request = HttpRequest() request.method='post' request.url = '/login/' # response = LoginView().post(request) response = LoginView.as_view()(request) print(response.content)
用戶中心登錄測驗
class LoginInfoTestCase(TestCase):
def setUp(self) -> None:
# 實體化物件屬性
self.factory_request = RequestFactory()
# 創建用戶
self.user = User.objects.create_user(
username='dilireba',
password='12345678',
)
def test_info(self):
# get方式請求用戶中心 創建factory物件
factory_request = self.factory_request.get('/info/')
# 攜帶用戶屬性 為登錄成功
factory_request.user = self.user
# 用factory模擬物件訪問用戶中心視圖函式
response = UserInfoView.as_view()(factory_request)
print(response.content.decode())
# 斷言檢查是否進入成功
code = json.loads(response.content.decode()).get('code')
self.assertEqual(code, 0, '訪問個人中心失敗!')
mock
簡介
-
概念
注意:老django版本中有這個模塊 后來直接封裝到python庫了
導包方式
django < 2.0版本 from django.test import mock
django > 2.0 版本 from unnitest import mockMock是Python中一個用于支持單元測驗的庫,它的主要功能是使用mock物件替代掉指定的用于發送請求的物件,以達到模擬物件的行為,
-
實作效果
import requests def c(url): response = requests.get(url)mock實作程序:
使用一個mock物件替換掉上面的requests.get函式,然后執行函式c時,c呼叫requests.get的回傳值就能夠由我們的mock物件來決定(若C函式執行成功<不用管具體的函式執行邏輯>接收函式回傳值,若不成功mock物件賦予默認值),而不需要服務器的參與,
-
適用場景
- 在專案開發程序中,盡早介入測驗->先把基本功能的輸入輸出測驗,
- 專案開發差不多,但是服務器架設進度緩慢,可以代替與服務器互動程序,提早測驗
mock實操
-
mock函式常用兩個引數說明
- side_effect 表示被呼叫的函式名
- 指向一個可呼叫的物件,一般是函式.當mock物件被呼叫時,如果該函式回傳值不是DEFAULT時,那么以該函式的回傳值作為mock物件呼叫的回傳值,
- 是例外類或者物件時,當mock被呼叫就會被拋出,
- 若是可迭代物件,mock每次呼叫就會回傳下一個值,若可迭代物件的元素是例外,就會拋出,
- return_value 表示被呼叫的函式失效情況下的回傳值
- 如果被呼叫的函式,功能暫未實作,可以通過return_value指定默認的回傳值,用來在測驗用例中進行斷言判斷
- side_effect 表示被呼叫的函式名
-
mock 小案例
1. mock基礎使用
# ==============被測驗的功能模塊=========== mport requests # side_effect 表示被呼叫的函式名 # return_value 表示被呼叫的函式的回傳值 # B功能->發請求 def send_req(url): print('B 功能....呼叫!') resp = requests.get(url) return resp.status_code pass # A功能->呼叫B def post_func(): return send_req('http://www.baidu.com') pass# =============測驗上述模塊:兩種情況====================== from . import utils from unittest import mock from django.test import TestCase class MockTestCase(TestCase): def test_B_None(self): """ 如果B模塊功能未實作""" # 1. 實體化mock物件 由于B功能未實作side_effect=None mock_obj = mock.Mock(side_effect=None, return_value=200) # 2. 替換send_req模塊 假設被post_func呼叫 utils.send_req = mock_obj # 3. 獲取回傳結果 因為設定了 return_value=200 所以回傳結果應該是200 results = utils.post_func() # 4. 斷言檢查是否為200 self.assertEqual(results, 200, 'B模塊測驗失敗!') def test_B_function(self): """如果B模塊實作""" # 1.實體化 mock物件 由于B功能實作side_effect=utils.send_req mock_obj = mock.Mock(side_effect=utils.send_req, return_value=300) # 2.替換 send_req 模塊 utils.send_req = mock_obj # 3. 獲取回傳結果 如果A功能呼叫B模塊成功 則回傳訪問baidu成功的狀態碼200 results = utils.post_func() # 4. 斷言 self.assertEqual(results, 200, 'B模塊測驗失敗!')2. mock 檢查是否被呼叫方法
class TestMockCalled(TestCase): def test_func(self): # 1.新建mock物件 mock_obj = mock.Mock(side_effect=utils.send_req, return_value=400) # 2.使用mock物件替換功能B函式 utils.send_req = mock_obj # 3.測驗功能B函式訪問百度是否成功 utils.visit_func() # 4.斷言mock物件是否被呼叫 self.assertEqual(mock_obj.called, True) # call('http://www.itcast.cn') self.assertEqual(mock_obj.call_args, True) # [call('http://www.itcast.cn')] self.assertEqual(mock_obj.call_args_list, True)3. mock 背景關系管理器用法
# ======測驗一個登陸功能的介面視圖函式是否可以正常被呼叫======= class LoginMockTest(TestCase): def test_class(self): """模擬類視圖1""" # 實體化mock物件 mock_obj = mock.Mock(return_value=200) # mock替換 LoginView.post = mock_obj print('>>>test_class run>>>') self.assertEqual(LoginView.post(), 200) def test_context_mock_use(self): # 1.實體化 模擬物件 mock_obj = mock.Mock(return_value=200) # 2.使用背景關系管理器實作視圖具體函式功能測驗 # 引數1表示被測驗的類,引數2表示測驗類中的方法,引數3表示mock物件 with mock.patch.object(LoginView, 'post', mock_obj): # 3.呼叫視圖函式 此時已經被mock物件替換 results = LoginView.post() print(results) # 4.斷言判斷 post方法是否可以呼叫 self.assertEqual(results, 200, '登錄失敗!')4. mock的裝飾器用法
-
reverse反向決議函式在django中的使用
# 路由正向 客戶端發請求->url->總路由->子路由->視圖函式 # 路由反向 視圖函式->路由 # 使用reverse的前提 # 1. 總路由需要起名稱 在使用的時候直接使用路由名稱即可 path('', include(('apps.xxx.urls'),namespace="這個總路由的名")) # 2. 子路由起名 path('login/', views.LoginView.as_view(), name='子路由的名'), # 3. 呼叫方法 reverse('總路由名稱:子路由明名稱') # 4. 后端重定向用法 redirect(reverse()) -
裝飾器形式的mock測驗用戶登錄
class ReverseTest(TestCase): def setUp(self) -> None: self.client = Client() self.url = reverse('User:Login') User.objects.create_user(username='wangyibo', password='11111111') # mock 裝飾器 寫法 # @mock.patch('精確模塊的路徑') @mock.patch('apps.users.views.LoginView') def test_mock_decorator_order(self, mock_obj): # 1.接收 mock物件 results = mock_obj # 2.指定 mock物件添加return_value屬性值 results.post.return_value = 200 # 3.添加測驗用戶 data_dict = {'username': 'wangyibo', 'password': '11111111'} # 4. 測驗用 client 發送請求 測驗 注意測驗的測驗物件是被mock替代了的視圖函式 side_effect=登錄的視圖函式 response = self.client.post(self.url, data=data_dict, content_type='application/json') print(response) # 4. 斷言 self.assertEqual(response.status_code, 200, '測驗失敗!') def test_reverse_url(self): # 反向決議輸出檢查 order_url = reverse('User:Login') print('反向決議的路由:', order_url)
-
Pytestest單元測驗
Pytest介紹
pytest是什么
- 是python的一個用于測驗的框架
- 適用于任何級別、任何代碼的測驗
pytest的優點
- 命令列模式工具,撰寫測驗用例簡單,豐富的檔案參考
- 支持單元測驗和功能測驗還有assert斷言 支持運行由Unittest撰寫的測驗Case
- 支持跳過某些測驗,對某些預期失敗的case標記為失敗
- 第三方插件豐富,可自定義
- 支持引數化
Pytest環境準備
安裝
# !!!最好切換到虛擬環境
workon xxx
pip install pytest
組態檔
pytest的組態檔通常放在測驗目錄下,名稱為pytest.ini,命令運行時會使用該組態檔中的配置
[pytest]
# 1.給 pytest 傳引數 三個插件的組態檔中寫寫法如下
# -s 輸出測驗用例中的print內容
# --html 插件 將測驗結果輸出為報告網頁
# --rerun碰見測驗報錯重新測驗幾遍
addopts = -s --html=./a.html --reruns 3
# 2.指定 測驗腳本的 路徑
testpaths = ./
# 3.指定 測驗檔案的 名字
python_files = test_a*.py
# 4.指定 測驗檔案類的 名字 當前的目錄下有指定名稱檔案才會執行
python_classes = Test*
# 5.指定 測驗檔案函式的 名字
python_functions = test_*
demo
# 檔案名 test_demo.py
import pytest
import requests
def test_one():
r = requests.get('http://baidu.com')
assert r.status_code == 200
def test_two():
r = requests.get('http://baidu.com')
assert r.encoding == 'utf'
# ======運行結果======
# 第一個測驗無報錯 第二個報錯 結果為 ISO-8859-1 != utf
運行方式
都是在終端環境下運行如下命令
-
pytest -s 運行當前目錄下的 所有測驗用例
- -s 作用 就是 如果測驗用例通過, 測驗函式中的print輸出列印內容
-
pytest test_xx.py 執行 test_xx.py檔案 中 所有測驗用例
-
pytest test_xx.py::類::函式 執行具體的某個測驗用例
-
pytest.main([’-s’]) 腳本執行
import pytest if __name__ == '__main__': pytest.main(['test_1.py','-s'])
Pytest相關函式使用
Pytest-assert+warning
-
原生斷言
import requests def test_assert(): age = 20 # 判斷 年齡是不是大于18歲 assert age >= 18, '未成年!' def test_jingdong(): response = requests.get('http://www.jd.com') print(response.content.decode()) assert response.status_code == 202, '沒成功'
- 例外測驗
# ====================測驗案例:判斷平年閏年================
def is_leap_year(year):
# 先判斷year是不是整型
if isinstance(year, int) is not True:
raise TypeError("傳入的引數不是整數")
# year 不能為0 的取值例外
elif year == 0:
raise ValueError("公元元年是從公元一年開始!!")
# year 必須為正整數的 型別例外
elif abs(year) != year:
raise ValueError("傳入的引數不是正整數")
# year的平潤年判斷
# 以下代碼 和 例外型別 無關
elif (year % 4 ==0 and year % 100 != 0) or year % 400 == 0:
print("%d年是閏年" % year)
return True
else:
print("%d年不是閏年" % year)
return False
# 一定要注意的是:站在測驗的角度去寫代碼
# 我們測驗報錯是否在業務邏輯中實作 而不是要看到測驗的報錯資訊 如果測驗OK不會有報錯的
# =================raise()方法的使用================
class TestException():
def test_ValueError1(self):
with pytest.raises(ValueError):
utils.is_leap_year(-5)
def test_TypeError(self):
with pytest.raises(TypeError) as info:
utils.is_leap_year(6.6)
# 這里也可以搭配 assert方法提示報錯資訊
assert info.type == TypeError
def test_ValueError2(self):
# match對應的錯誤資訊 因為數值錯誤有兩個 可能會匹配到其中任意一個
# 如果 例外型別 相同 對比不出來, 需要借助例外中的提示資訊 二次
# match的實作底層借用 re.search() 方法 尋找符合條件的字串提示資訊進行具體的錯誤型別匹配
with pytest.raises(ValueError, match="公元元年"):
utils.is_leap_year(0)
def test_ValueError3(self):
with pytest.raises(ValueError, match="正整數"):
utils.is_leap_year(-9)
def test_leap_year(self):
assert utils.is_leap_year(2001) == True, "不是閏年"
- 警告測驗
# =================測驗案例:幾個警告的提示函式=============
class MakeWarns():
# 1. 棄用警告 函式
def make_decotat_warns(self):
warnings.warn('deprecate', DeprecationWarning)
# 2. 用戶警告 函式
def make_user_warns(self):
warnings.warn('user', UserWarning)
# 3. 語法警告 函式
def make_syntax_warns(self):
warnings.warn('syntax', SyntaxWarning)
# 4. 位元組警告 函式
def make_bytes_warns(self):
warnings.warn('bytes', BytesWarning)
# 5. 運行時 runtime警告 函式
def make_runtime_warns(self):
warnings.warn('runtime', RuntimeWarning)
# 6. 無警告 函式
def not_warns(self):
pass
# 7. 很多警告
def many_warns(self):
warnings.warn('runtime', RuntimeWarning)
warnings.warn('user', UserWarning)
# ===================測驗用例============
import warning
class TestWarnsType():
def test_deprecate(self):
# 測驗棄用警告
with pytest.warns(DeprecationWarning):
MakeWarns().make_decotat_warns()
def test_bytes(self):
# 測驗位元組警告
with pytest.warns(BytesWarning):
MakeWarns().make_bytes_warns()
def test_sync(self):
# 測驗語法警告
with pytest.warns(SyntaxWarning, match='syntax'):
MakeWarns().make_syntax_warns()
def test_many_warning(self):
# 測驗 很多警告 中Runtime是否生效
with pytest.warns(RuntimeWarning) as record:
MakeWarns().many_warns()
def test_as(self):
"""warning的幾個資訊查看用法"""
with pytest.warns(UserWarning) as warninfo:
make_warning.warn_message()
print('\n', warninfo)
print(len(warninfo))
print(warninfo[0].message)
print(warninfo[1].message)
assert len(warninfo) == 2
assert str(warninfo[0].message) == "user"
assert str(warninfo[1].message) == "runtime"
Pytest-setup/teardown
類似于django的Teatcase中的setUp和tearDown函式
一、函式分類及介紹
- 函式級別
- setup_function和teardown_function
- 只針對測驗類 外面的 測驗函式 每個函式執行前后都會執行
- setup_method和teardown_method
- 只針對測驗類 里面的 測驗函式 類中每個函式執行前后都會執行
- setup_function和teardown_function
- 類
- setup_class和teardown_class
- 一個測驗用例類 只呼叫一次
- setup_class和teardown_class
- 模塊
- setup_module和teardown_module
- 一個py測驗檔案只執行一次
- setup_module和teardown_module
二、優先級順序
- 測驗代碼中不建議同時全部使用
- 先后順序
- setup執行先后順序
- module>class>method 類里面的測驗函式
- module>function 單獨測驗函式的
- teardown執行先后順序
- method>class>module 類里面的測驗函式
- function>moudle 單獨測驗函式的
- setup執行先后順序
三、Demo演示
# =============優先級查看代碼=============
def setup_module():
print('\n>> setup_module setup_module ...')
def teardown_module():
print('\n>> teardown_module teardown_module ...')
def test_one():
print('\n>>> test_one 111....')
def test_two():
print('\n>>> test_two 222....')
class TestSetUpMethod():
def setup_class(self):
print('\n>> setup_class ...')
def teardown_class(self):
print('\n>> teardown_class ...')
def setup_method(self):
print('\n>>> setup_method ...')
def teardown_method(self):
print('\n>>> teardown_method ...')
def test_three(self):
print('\n>>> test_three 333....')
def test_four(self):
print('\n>>> test_four 444....')
class TestSetUpMethodTwo():
def test_three(self):
print('\n>>> test_three 555....')
def test_four(self):
print('\n>>> test_four 666....')
# =================輸出結果==============
setup_module setup_module ...
>>> test_one 111....
.
>>> test_two 222....
.
>> setup_class ...
>>> setup_method ...
>>> test_three 333....
.
>>> teardown_method ...
>>> setup_method ...
>>> test_four 444....
.
>>> teardown_method ...
>> teardown_class ...
>>> test_three 555....
.
>>> test_four 666....
.
>> teardown_module teardown_module ...
Pytest-fixture
一 、fixture是什么
- fixture是pytest特有的功能,它用pytest.fixture標識,以裝飾器形式定義在函式上面,在撰寫測驗函式的時候,可以將此函式名稱做為傳入引數,pytest將會以依賴注入方式,將該函式的回傳值作為測驗函式的傳入引數.
二、 場景舉例
- 如果測驗類中 5個測驗用例 , 其中 有 3個測驗用例需要登錄, 2個不需要
- setup_method 可以實作 但是不夠靈活
- 直接使用fixture就可以實作哪個需要給哪個
三、 使用
@pytest.fixture()
def user_login():
# 實作用戶登錄
print('用戶登錄成功')
# 測驗用例:有些要求用戶必須登錄,有些要求可以不登錄訪問
def test_user_orders(user_login):
print('>>>test_user_orders run >>>')
def test_payment_status(user_login):
print('>>>test_payment_status run >>>')
# 專案首頁,任何人都可以訪問
def test_index():
print('>>>test_index run >>>')
# ============不同級別情況下使用fixture的執行次數==========
# function級別共執行3次
# class級別共執行2次
# module級別共執行1次
# session級別共執行1次
# 定義fixture
@pytest.fixture(scope='function')
def user_login():
# 實作用戶登錄
print('用戶登錄成功')
# 在類外面使用fixture,在類外面依然可以使用
def test_1(user_login):
print('>>>test_1 run>>>')
# 在類外面使用fixture,在類里面依然可以使用
class TestLogin(object):
# 測驗用例:有些要求用戶必須登錄,有些要求可以不登錄訪問
def test_user_orders(self, user_login):
print('>>>test_user_orders run >>>')
def test_payment_status(self, user_login):
print('>>>test_payment_status run >>>')
四、fixture函式共享
- 作用
- 給所有的測驗用例添加可以直接呼叫的功能
- 實作方式
- 在測驗檔案目錄下新建 conftest.py 檔案 (檔案名不能修改!!!)
- 在這個檔案中定義 通用的 可能需要被測驗類呼叫的函式 pytest執行測驗時會自動呼叫
- demo舉例
# ==========================檔案名:conftest.py======================
import pytest
"""
function:對所有的測驗函式生效,不同的測驗檔案中執行多次;
class:對所有的測驗類和測驗函式生效,不同的測驗檔案中執行多次
module:對所有的測驗類和測驗函式生效,一個測驗檔案,只執行一次
session:對所有的測驗類和測驗函式生效,多個測驗檔案,只執行一次
"""
# 添加功能: 登錄成功輸出資訊
@pytest.fixture(scope='function')
# @pytest.fixture(scope='class')
# @pytest.fixture(scope='module')
# @pytest.fixture(scope='session')
def user_login():
print('\n用戶登錄成功\n')
# ========================檔案名:fixture_test.py===============
import pytest
# 使用共享檔案中的fixture裝飾的的user_login的功能函式
def test_1(user_login):
print('\n>>> test_1 run >>>\n')
def test_2():
print('\n>>> test_2 run >>>\n')
# fixture標記函式共享檔案的使用
@pytest.mark.usefixtures('user_login')
class TestOrders(object):
"""
fixture標記函式共享檔案的使用:
1.可以直接傳給測驗類中的指定測驗函式,只對測驗函式生效
2.也可以直接把測驗類使用fixture進行標記,對該類中的所有測驗函式都生效
"""
def test_get_orders(self):
print('\n>>> test_get_orders run >>>\n')
def test_get_goods(self):
print('\n>>> test_get_goods run >>>\n')
五、fixture的引數化
-
三個引數說明
- scope 作用域:即 function、class、module、session 這四個級別
- function:即測驗 類外的測驗函式、類中的測驗用例函式 運行多次
- class:即作用測驗用例類 一個類一次
- Module:作用整個py模塊 一個模塊一次
- Sessioon:作用整個一次的測驗包含所有的測驗用例 pytest一次
- param:List型別,默認None, 接收引數值,對于param里面的每個值,fixture都會去遍歷執行一次.
- autouse:Bool型別,默認Ture 為true時此session中的所有測驗函式都會呼叫fixture
- scope 作用域:即 function、class、module、session 這四個級別
-
demo
import pytest from _pytest.fixtures import SubRequest # 1.多個引數串列 user_list = ['dlireba', 'oyangnanan'] pwd_list = ['111111', '222222'] # 2.多個fixture # params引數的用法 @pytest.fixture(params=user_list) def user_name(request): return request.param @pytest.fixture(params=pwd_list) def pwd_content(request): return request.param # scope引數的用法 # @pytest.fixture(scope='function') @pytest.fixture(scope='class') # @pytest.fixture(scope='module') # @pytest.fixture(scope='session') def pwd_content(request): return 'scope測驗' # 3.給測驗函式 多個fixture class TestOrders(object): # 呼叫 fixture裝飾的函式 會自動遍歷匹配 一共會生成4個測驗用例 def test_get_orders(self, user_name, pwd_content): print('\n從fixture傳過來的name資料', user_name) print('從fixture傳過來的pwd資料', pwd_content) print('>>> test_fixture2 run >>>\n') # === autouse引數 一般情況下不用,影響測驗靈活性====== # True 一旦執行pytest后的所有的測驗用例將會自動被傳遞hell()函式 # False 默認關閉此功能 @pytest.fixture(autouse=True) def hello(): print('\nhello world 2020\n')
Pytest-mark
一、作用以及常用的5個標記函式
-
作用
標記測驗用例,以滿足在某些特定場景下測驗用例的特殊要求實作的功能
-
6個標記函式
- pytest.mark.usefixtures 給測驗用例類 添加fixture
- pytest.mark.xfail 標識測驗用例預期失敗
- pytest.mark.skip 沒有限制條件情況下跳過測驗用例
- pytest.mark.skipif 有限制條件情況下跳過測驗用例
- pytest.fixture(params=[]) 直接給fixture函式傳遞引數
- pytest.mark.parametrize() 直接給測驗用例函式傳遞引數
二、6個標記函式的使用
import pytest
# fixture標記函式共享檔案的使用
@pytest.mark.usefixtures('user_login')
class TestMark():
def test_one(self):
print('\n test_one .....')
assert 1
"""
pytest.mark.xfail()
如果不寫引數 默認預期失敗
如果有引數 condition reason 必須都寫
"""
@pytest.mark.xfail(condition=False, reason="測驗 登錄失敗! ")
def test_two(self):
print('\n test_two .....')
assert 0, '測驗失敗了!'
"""
無條件 跳過
"""
@pytest.mark.skip()
def test_three(self):
print('\n test_three .....')
assert 1
"""
有條件 跳過
pytest.mark.skipif()
如果 不寫引數 --默認就跳過
寫了引數 必須兩個一起寫 condintion reason
"""
@pytest.mark.skipif(condition=True, reason='跳過的原因')
def test_four(self):
print('\n test_four .....')
assert 1
username = "user,pwd"
values = [('reba', '12345678'), ('oyangnanan', '99999999')]
"""
key -argnames 引數型別-string -- "k1,k2,k3"
value -argvalues 引數型別-list -- [v1,v2,v3]
"""
@pytest.mark.parametrize(username, values)
def test_five(self, user, pwd):
print('\n test_five .....')
print('\n 測驗函式接收外界的引數:', user)
print('\n 測驗函式接收外界的引數:', pwd)
print('\n parametrize測驗用例 .....')
assert 1
"""
之前的fixture引數傳遞
"""
@pytest.fixture(params='fixture_param_test')
def test_six(self, request):
print('\n test_six .....')
return request.param
# 傳遞后使用
def test_seven(self, test_six):
print('\n test_seven .....')
print('\nfixture的param測驗:', test_six)
assert 1
Pytest的插件
三個常用插件: pytest-html pytest-orderingrun pytest-rerunfailures
安裝:
pip install pytest-html -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pytest-ordering -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pytest-rerunfailures -i https://pypi.tuna.tsinghua.edu.cn/simple
Pytest-html-生成測驗報告
兩種使用方式
- 終端命令列:pytest 測驗檔案.py --html=存盤路徑/report.html
- 組態檔中修改:appopts= -s --html=./report.html
Pytest-orderingrun-控制函式執行順序
import pytest
# 通過插件,指定測驗函式執行的順序
"""
正整數:值越小,先執行
負數:值越小,先執行
正負數:優先執行正數,最后執行負數,值越小,先執行
0:優先執行0,然后正數、負數
"""
class TestOrdering(object):
# 默認從上往下執行 !!直接使用標記函式的方式進行呼叫!!
@pytest.mark.run(order=1)
def test_login(self):
print('\n>>>登錄執行>>>\n')
@pytest.mark.run(order=0)
def test_register(self):
print('\n>>>注冊執行>>>\n')
@pytest.mark.run(order=2)
def test_user_info(self):
print('\n>>>獲取用戶資訊執行>>>\n')
Pytest-rerunfailures-重測驗失敗的用例
**作用 **
- 就只在某種情況下(網路阻塞、軟體卡頓),測驗用例測驗程序例外測驗失敗,進行自動重新測驗
兩種使用方式
- 終端命令列:pytest 測驗檔案.py --reruns 重試次數
- 組態檔中修改:appopts= -s --reruns 重試次數
Yaml
Yaml是什么
一、介紹
? 一種支持所有編程語言的序列化標準,用來存盤資料,類似于json
二、語法規則
- 大小寫敏感
- 縮進表示層級關系, 不建議使用tab鍵 縮進空格數量不重要,重要的是同層級對齊即可
三、種資料型別
-
object(物件):
鍵值對的集合,又稱為映射/hash/字典 —> python中dict
dilireba: 18 oyangnana: kavayi wangyibo: cool -
array(陣列):
一組按照次序排列的值,又稱序列(sequence)、串列 —> python中list
dilireba: [18, 18, 18] nazha: - 18 - 18 - 18 -
scalars(純量):
單個的、不可再分的值, 包括 字串、布林值、整數、浮點數、null、日期 —>python中的不可拆分的資料型別
animal: pets # !!!!!注意冒號后面必須要有一個空格!!! dog: beibei # 字串 value: "hello" 轉換為python代碼 {"value":"hello"} # 布林值 value1: true value2: false 轉換為python代碼 {'value1': True, 'value2': False} # 整數,浮點數 value1: 12 value2: 12.102 # 轉換為python代碼 {'value1': 12, 'value2': 12.102} # 空(Null) value1: ~ # ~ 表示為空 轉換為python代碼 {'value1': None} # 日期 value1: 2017-10-11 15:12:12 轉換為python代碼 {'languages': {'value1': datetime.datetime(2017, 10, 11, 15, 12, 12)}} -
錨點的使用
錨點類似于一個變數,只不過其中只存盤著value供別人呼叫
data: &ok # & 是錨點標示符號 value: 456 name: value1: 123 <<: *ok # "<<:" 合并到當前位置,"*imp" 參考錨點imp # 轉換為python代碼 注意字典是無序的 # 錨點本身字典也會被pyYAML轉換(下節) {'data': {'value': 456}, 'name': {'value': 456, 'value1': 123}}
PyYAML決議
一、作用
- 一個python庫用于對yaml檔案進行決議,決議為字典格式的資料
- yaml->json->dict
二、安裝
- pip3 install PyYAML
三、實操
# ========================yaml格式檔案===================
data_one: zhangsan
data_dict: {'user':'A', 'pwd':'123456'}
data_list:
- a
- b
- c
data_list_two: [1,2,3,4]
# 純量 單個變數
str_one: "abcdefg"
bool_data: true
int_data: 100
float_data: 1.2
# 轉換后 呈現格式都是 None
null_data: ~ # ~
null_one: null
null_two: None
datetime_data: 2020-10-13 16:14:24
date_two: 2020-10-13
# 這種時間格式不支持 會轉換成秒數輸出
time_data: 16:14:28
# ===========================測驗代碼=======================
import yaml
from pprint import pprint
def test_parse_yaml_file1():
f = open('test.yaml', 'r')
# data = f.read()
# 1.讀取決議 yaml檔案的資料 Loader默認為空 但是不賦值會有警告
data = yaml.load(f, Loader=yaml.FullLoader)
# 字典 格式
print(type(data))
# 2.將資料 寫入yaml檔案
f_write = open('test.yaml', 'a')
# 新建一個字典資料
data_dict = {'username': '吳亦凡'}
# 轉換格式寫入
yaml.dump(data_dict, f_write, allow_unicode=True)
def test_parse_yaml_file2():
# 打開
f = open('test.yaml', 'r')
# 轉換為字典
data = yaml.load(f, Loader=yaml.FullLoader)
# 優化列印出來的資料效果 pprint
pprint(data)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/172480.html
標籤:其他
