一、概述1. 課程概述2. 課程安排二、接口測試1.【知道】什么是接口測試2. 環境准備3. Postman的基本使用3.1【掌握】借助Postman完成接口測試3.2【掌握】導出導入Postman測試記錄4. Requests的使用4.1 發送GET請求4.1.1【掌握】發送GET請求4.1.2【掌握】響應狀態碼、響應頭、響應體4.1.3【知道】字符編碼4.1.4【掌握】resp.json4.1.5【知道】構造請求頭和cookies4.2【掌握】發送POST請求4.3【掌握】requests和requests.session的區別三、基於django的單元測試1.【知道】認識單元測試2.【掌握】編寫和運行django的單元測試3. TestCase類3.1【知道】前后置方法運行特點3.2【掌握】setUpClass 和 tearDownClass應用場景4. Client類4.1 Client介紹4.2【知道】Client的使用4.3 測試django自帶的用戶驗證系統5. 斷言5.1【知道】斷言介紹5.2【掌握】斷言的使用
一、概述
1. 課程概述
2. 課程安排
二、接口測試
1.【知道】什么是接口測試

2. 環境准備
-
創建虛擬環境
-
下載模塊
-
運行項目代碼
3. Postman的基本使用
3.1【掌握】借助Postman完成接口測試
json格式數據如何發送

3.2【掌握】導出導入Postman測試記錄
4. Requests的使用
-
Requests是一個功能強大的HTTP Client,能夠幫助我們發送HTTP請求,並獲取響應
4.1 發送GET請求
4.1.1【掌握】發送GET請求
import requests
url = 'http://127.0.0.1:8080/user'
# 響應對象 = requests.get(url)
resp = requests.get(url)
print(resp.text)
4.1.2【掌握】響應狀態碼、響應頭、響應體
# 響應狀態碼、響應頭、響應體
print(resp.status_code) # 401
print(resp.headers) # 響應頭
print(resp.text, type(resp.text)) # {"error": "用戶未登錄"} <class 'str'>
print(resp.content) # 二進制
print(resp.json()) # 是字典類型,如果返回的格式不是json,發生異常, JSONDecodeError
4.1.3【知道】字符編碼
# 查看編碼
print(resp.encoding) # ISO-8859-1
# 顯示中文,修改編碼格式為utf-8
resp.encoding = 'utf-8'
4.1.4【掌握】resp.json
print(resp.json()) # 是字典類型,如果返回的格式不是json,發生異常, JSONDecodeError
4.1.5【知道】構造請求頭和cookies
# 構造header
_headers = {'User-Agent':'hello mike'}
resp = requests.get(url, headers=_headers)
print(resp.text) # {"User-Agent": "hello mike", "Cookies": {}}
_cookies = {
'k1':'v1',
'k2':'v2'
}
resp = requests.get(url, headers=_headers, cookies=_cookies)
print(resp.text)
4.2【掌握】發送POST請求
import requests
# 如果以表單格式提交數據,使用data關鍵字參數
# 如果以json格式提交數據,使用json關鍵字參數
# 構建用戶登陸數據
user_info = {
'username':'mike123',
'password':'12345678'
}
# 發送Post請求
resp = requests.post('http://127.0.0.1:8080/login', data=user_info)
# 獲取響應內容
print(resp.json())
4.3【掌握】requests和requests.session的區別
-
requests不會狀態保持
-
requests.session可以狀態保持
import requests
# 實例化一個 requests.Session() 對象
# session對象發送數據,發送請求方式和requests一樣
session = requests.Session()
# 登陸
login_url = 'http://127.0.0.1:8080/login'
# 構建用戶登陸數據
user_info = {
'username':'mike123',
'password':'12345678'
}
resp = session.post(login_url, data=user_info)
print(resp.json())
# 獲取登陸用戶的信息
resp = session.get("http://127.0.0.1:8080/user")
print(resp.json())
三、基於django的單元測試
1.【知道】認識單元測試
-
單元測試是用來對一個模塊、一個函數或者一個類來進行正確性檢驗的測試工作。
-
單元測試側重於代碼邏輯,接口測試側重於業務邏輯
2.【掌握】編寫和運行django的單元測試
-
寫的代碼集成在項目代碼中,創建test開頭的文件
-
類繼承
django.test.TestCasefrom django.test import TestCase
class MyTestCase(TestCase):
# 1. 前置、后置方法,名字固定
def setUp(self) -> None:
print('前置setUp2')
def tearDown(self) -> None:
print('后置tearDown2')
# 2. 測試用例,test開頭的方法
def test_1(self):
print('測試用例12')
def test_2(self):
print('測試用例22') -
命令運行
# 執行項目中全部的測試用例,項目中所有以test開頭的py文件都會執行 python manage.py test # 運行某個名為users的app下面的所有的測試用例,users是應用的名字(APP_name),應用中所有以test開頭的py文件都會執行 python manage.py test users # 運行某個app下面的tests.py文件 python manage.py test users.tests # 運行某個app下面的tests.py文件中指定的class類 python manage.py test users.tests.MyClassTestCase # 執行ModeTest類下的某個測試方法 python manage.py test users.tests.MyClassTestCase.test_my_func
3. TestCase類
3.1【知道】前后置方法運行特點
-
類方法的前后置方法:在類啟動、結束調用
-
實例前后置方法:test開頭的實例方法前后調用
from django.test import TestCase
class MyTestCase(TestCase):
# 1. 前置、后置方法,名字固定
# 針對於test開頭的實例方法
def setUp(self) -> None:
print('前置setUp\n')
def tearDown(self) -> None:
print('后置tearDown\n')
@classmethod
def setUpClass(cls):
print('類方法setUpClass')
@classmethod
def tearDownClass(cls):
print('類方法tearDownClass')
# 2. 測試用例,test開頭的方法
def test_1(self):
print('測試用例1')
def test_2(self):
print('測試用例2')
3.2【掌握】setUpClass 和 tearDownClass應用場景
# 定義 setUpClass: 用戶登錄
# 定義 tearDownClass: 用戶退出
# 定義測試方法:獲取登陸用戶、查看UA和Cookie
from django.test import TestCase
from requests import Session
class DjangoTest(TestCase):
# 定義 setUpClass: 用戶登錄
@classmethod
def setUpClass(cls):
# 實例化對象, cls.session類屬性
cls.session = Session()
# 登陸
login_url = 'http://127.0.0.1:8080/login'
# 構建用戶登陸數據
user_info = {
'username': 'mike123',
'password': '12345678'
}
resp = cls.session.post(login_url, data=user_info)
print("登陸", resp.json())
# 定義 tearDownClass: 用戶退出
@classmethod
def tearDownClass(cls):
resp = cls.session.get('http://127.0.0.1:8080/logout')
print("登出", resp.json())
# 查看用戶信息
def test_user(self):
resp = self.session.get('http://127.0.0.1:8080/user')
print("用戶信息", resp.json())
# 查看cookies
def test_header(self):
resp = self.session.get('http://127.0.0.1:8080/header')
print("頭部信息", resp.json())
4. Client類
4.1 Client介紹
-
使用和requests.session一樣
-
可以狀態保持
-
不用啟動服務也可以使用,數據不會保存在數據庫,放在臨時的數據庫
4.2【知道】Client的使用
-
雖然不用啟動后台服務,但是依賴於后台代碼,所以,接口協議按照后台的規定
-
由於沒有啟動服務,所以,不需要url
-
其它用法和requests.Session用法一樣
# 注冊
# 登錄
# 查看UA和Cookie
# 登出
# 再次查看UA和Cookie
from django.test import TestCase, Client
class DjangoTest(TestCase):
def setUp(self) -> None:
print('setUp')
# 共享實例屬性的數據
self.user_info = {
'username': 'mike123',
'password': '12345678',
'mobile': '13344445555'
}
# 實例化client對象
self.client = Client()
def test_api(self):
print('注冊、登陸、查看、退出、查看')
self.do_register()
self.do_login()
self.do_header()
self.do_logout()
self.do_header()
def do_register(self):
print('注冊')
resp = self.client.post('/register', data=self.user_info)
print(resp.json())
def do_login(self):
print('登陸')
resp = self.client.post('/login', data=self.user_info)
print(resp.json())
def do_header(self):
print('查看')
resp = self.client.get('/header')
print(resp.json())
def do_logout(self):
print('登出')
resp = self.client.get('/logout')
print(resp.json())
4.3 測試django自帶的用戶驗證系統
def do_login(self):
print('登陸')
# 項目使用django自帶的用戶驗證系統,可用login函數和logout函數進行測試
resp = self.client.login(username=self.user_info['username'],
password=self.user_info['password'])
print(resp)
def do_logout(self):
print('登出')
self.client.logout()
5. 斷言
5.1【知道】斷言介紹
-
測試結果是否符合預期,如果符合,說明測試通過(斷言成功),如果不符合測試失敗(斷言失敗)
# 檢查是否登錄成功
if result_code != 200:
# 只要狀態碼不是200,說明登陸失敗,不符合預期,拋出斷言異常
raise AssertionError(f"實際的結果是{result_code},預期的結果是:200")
5.2【掌握】斷言的使用
# 如果result_code和200相同,說明符合預期,測試通過,msg不會顯示
# 否則,測試不通過,msg顯示
self.assertEqual(result_code, 200, msg='狀態瑪不為200,測試不通過')
1. Pytest介紹2.【掌握】Pytest 第一個例子3. 斷言的處理3.1【掌握】assert斷言語句3.2 異常斷言3.2.1【掌握】pytest.raises()的使用3.2.2【知道】把異常信息存儲到變量3.2.3 通過異常的內容捕捉異常3.3 警告斷言3.3.1【掌握】pytest.warns()的使用3.3.2【知道】把警告信息存儲到變量3.3.3 通過警告的內容捕捉警告4. setup和teardown函數4.1【知道】函數級別4.2【知道】類級別4.3【知道】模塊級別5. pytest.fixture基本使用5.1【理解】fixture基本使用5.2【掌握】fixture裝飾的函數5.3 conftest.py文件5.3.1【掌握】共享fixture函數5.3.2【掌握】pytest.mark.usefixtures6. pytest.fixture參數6.1【知道】scope參數6.2 params參數6.2.1【理解】案例16.2.2【理解】案例26.3【知道】autouse參數7. pytest.mark標記7.1【知道】pytest.mark.xfail()7.2【知道】pytest.mark.skip()7.3【知道】pytest.mark.skipif()7.4【知道】pytest.mark.parametrize()
1. Pytest介紹
-
默認自動識別當天目錄下test開頭的函數和方法、Test開頭的類
import pytest
import requests
# 定義2個test開頭的函數
def test_one():
print('test_one')
def test_two():
print('test_two')
# 定義一個Test開頭的類
class TestClass(object):
# 定義2個test開頭的實例方法
def test_1(self):
print('11111111111111111111111')
def test_2(self):
print('222222222222222')
if __name__ == '__main__':
# pytest.main() # 默認允許同一個目錄下,test開頭的文件,默認不支持打印,需要配置才能打印
# pytest.main(['-s']) # -s, 可以打印
pytest.main(['-s', 'test_1.py'])
-
pytest自動失敗test開頭的函數和方法,或者Test開頭的類
pytest.main() # 不打印運行同目錄所有test文件
pytest.main(['-s']) # 可以打印
pytest.main(['-s', 'test_1.py']) # 只運行test_1.py文件 -
命令行運行
-
pytest-
會執行當前文件的同時,路徑下所有以test開頭的py文件都會被執行
-
pytest先獲取所有文件中的測試函數后,再執行
-
-
不會顯示代碼中的標准輸出(print、logging)
-
-
pytest -s-
會執行當前文件的同時,路徑下所有以test開頭的py文件都會被執行
-
pytest先獲取所有文件中的測試函數后,再執行
-
-
顯示代碼中的標准輸出(print、logging)
-
-
pytest test_1.py-
只會執行當前文件
-
不會顯示代碼中的標准輸出(print、logging)
-
-
pytest -s test_1.py-
只會執行當前文件
-
顯示代碼中的標准輸出(print、logging)
-
3. 斷言的處理
3.1【掌握】assert斷言語句
# django自帶斷言 resp.status_code, 200, msg=‘提示信息’
# assert 條件表達式, ’提示信息‘
assert resp.status_code == 200, '測試不通過,狀態碼不是200'
3.2 異常斷言
3.2.1【掌握】pytest.raises()的使用
"""
# 產生的異常和預期要捕獲到的異常一樣,則測試通過
with pytest.raises(預期要捕獲到的異常):
產生的異常
"""
class TestAssert(object):
def test_assert(self):
with pytest.raises(TypeError):
is_leap_year("2020")
3.2.2【知道】把異常信息存儲到變量
def test_assert2(self):
with pytest.raises(TypeError) as err_info:
is_leap_year("2020")
err_info.__dict__ # 查看對象內部成員信息
assert err_info.type==TypeError, '捕獲到的異常不匹配'
3.2.3 通過異常的內容捕捉異常
# 通過異常提示信息捕獲指定異常
with pytest.raises(BaseException, match='不是整數') as err_info:
is_leap_year("2020")
if err_info.type == TypeError:
print('aaaaaaaaaaa')
3.3 警告斷言
-
捕獲警告,捕獲到警告,測試通過
-
可以同時捕獲多個警告
3.3.1【掌握】pytest.warns()的使用
3.3.2【知道】把警告信息存儲到變量
class TestWarn(object):
def test_1(self):
with pytest.warns(UserWarning):
warnings.warn("value must be 0 or None", UserWarning)
def test_2(self):
# 由於捕獲了2個警告,warn_info就是列表
with pytest.warns((UserWarning, RuntimeWarning)) as warn_info:
warnings.warn("value must be 0 or None", UserWarning)
warnings.warn("value must11111", RuntimeWarning)
# print(warn_info.__dict__)
print(len(warn_info))
# {'message': UserWarning('value must be 0 or None',),
# 'category': <class 'UserWarning'>, 'filename': '/home/python/code/pytest_code/test_4_warn.py', 'lineno': 25, 'file': None, 'line': None, 'source': None, '_category_name': 'UserWarning'}
print(warn_info[0].__dict__)
print(warn_info[0].message)
print(warn_info[0].category)
3.3.3 通過警告的內容捕捉警告
>> > with warns(UserWarning, match='must be 0 or None'):
...
warnings.warn("value must be 0 or None", UserWarning)
>> > with warns(UserWarning, match=r'must be \d+$'):
...
warnings.warn("value must be 42", UserWarning)
>> > with warns(UserWarning, match=r'must be \d+$'):
...
warnings.warn("this is not here", UserWarning)
4. setup和teardown函數
4.1【知道】函數級別
# 函數級別的前、后置函數,只針對普通測試用例
def setup_function():
print('setup_function')
def teardown_function():
print('teardown_function')
def test_1():
print('普通測試用例111111')
def test_2():
print('普通測試用例111111')
class TestClass(object):
def test_one(self):
print('類實例方法測試用例111111')
4.2【知道】類級別
class TestClass(object):
# 針對類的前置后置方法
@classmethod
def setup_class(cls):
print('setup_class')
@classmethod
def teardown_class(cls):
print('teardown_class')
# 針對測試實例方法
def setup_method(self):
print('setup_method')
def teardown_method(self):
print('setup_method')
def test_one(self):
print('類實例方法測試用例111111')
def test_two(self):
print('類實例方法測試用例22222')
-
類的前置后置方法:在類啟動、結束運行
-
測試實例方法:類中測試實例方法調用前、調用結束運行
4.3【知道】模塊級別
# 針對模塊
def setup_module():
print('setup_module')
def teardown_module():
print('teardown_module')
# 針對普通測試函數
def setup_function():
print('setup_function')
def teardown_function():
print('teardown_function')
def test_1():
print('普通測試用例111111')
def test_2():
print('普通測試用例22222')
class TestClass(object):
# 針對類的前置后置方法
@classmethod
def setup_class(cls):
print('setup_class')
@classmethod
def teardown_class(cls):
print('teardown_class')
# 針對測試實例方法
def setup_method(self):
print('setup_method')
def teardown_method(self):
print('teardown_method')
def test_one(self):
print('類實例方法測試用例111111')
def test_two(self):
print('類實例方法測試用例22222')
運行結果:
test_8.py setup_module setup_function 普通測試用例111111 .teardown_function setup_function 普通測試用例22222 .teardown_function setup_class setup_method 類實例方法測試用例111111 .teardown_method setup_method 類實例方法測試用例22222 .teardown_method teardown_class teardown_module
5. pytest.fixture基本使用
5.1【理解】fixture基本使用

# 0.@pytest.fixture裝飾函數
@pytest.fixture()
def get_web_url():
print('get_web_url')
return 'http://www.baidu.com'
# 1. 把上面函數名作為測試用例的參數
def test_web(get_web_url):
# 2. 測試用例調用前,需要先確定形參get_web_url,就是調用get_web_url
print('test_web')
print(get_web_url) # 測試用例內部使用get_web_url,就是使用它返回值
r = requests.get(get_web_url)
assert r.status_code == 200, '測試失敗'
5.2【掌握】fixture裝飾的函數
@pytest.fixture()
def foo():
print('\nfoo: 1111111111111')
return 'http://itcast.cn'
@pytest.fixture()
def bar(foo):
resp = requests.get(foo)
print('bar: 22222222222222')
return resp
def test_web(bar):
print('333333333333')
# 響應狀態碼應該是200, 否則會斷言失敗
assert bar.status_code == 200
if __name__ == '__main__':
pytest.main(['-s', 'test_10.py'])
5.3 conftest.py文件
5.3.1【掌握】共享fixture函數

5.3.2【掌握】pytest.mark.usefixtures

6. pytest.fixture參數
6.1【知道】scope參數
-
function:設置為function,表示每個測試方法都要執行一次, 普通測試用例、類里面的測試用例都可以執行
-
class
# 普通測試方法,正常執行,類里面的測試實例方法,只會執行1一次
import pytest
@pytest.fixture(scope='class')
def foo():
print('foo')
def test_1(foo):
print('普通測試用例111111')
def test_2(foo):
print('普通測試用例22222')
class TestClass(object):
def test_one(self, foo):
print('類實例方法測試用例111111')
def test_two(self, foo):
print('類實例方法測試用例22222')
# module:只會在傳參的時候執行1次,針對test開頭的文件
# session:只會在session開始的時候執行1次,在一個文件夾啟動所有測試文件,session是整個文件夾
import pytest
# @pytest.fixture(scope='module')
@pytest.fixture(scope='session')
def foo():
print('foo')
def test_1(foo):
print('普通測試用例111111')
def test_2(foo):
print('普通測試用例22222')
class TestClass(object):
def test_one(self, foo):
print('類實例方法測試用例111111')
def test_two(self, foo):
print('類實例方法測試用例22222')
6.2 params參數
6.2.1【理解】案例1
import pytest
def check_password(password):
"""
檢查密碼是否合法
:param password: 長度是 8 到 16
:return:
"""
pwd_len = len(password)
if pwd_len < 8:
return False
elif pwd_len > 16:
return False
else:
return True
# 參數化
@pytest.fixture(params=['1234', '1312532532', '1325215', '135325'])
def get_pwd(request): # request固定
return request.param
# 測試用例
def test_pwd_len(get_pwd):
print(get_pwd)
print(check_password(get_pwd))
6.2.2【理解】案例2
import pytest
# 參數化
@pytest.fixture(params=['mike123', 'mike666', 'admin12'])
def get_user(request): # request固定
return request.param
@pytest.fixture(params=['12345678', 'mike66611', 'admin1211'])
def get_pwd(request): # request固定
return request.param
# 測試用例
def test_login(get_user, get_pwd):
print(get_user, ' ===> ', get_pwd)
test_15.py mike123 ===> 12345678 .mike123 ===> mike66611 .mike123 ===> admin1211 .mike666 ===> 12345678 .mike666 ===> mike66611 .mike666 ===> admin1211 .admin12 ===> 12345678 .admin12 ===> mike66611 .admin12 ===> admin1211
6.3【知道】autouse參數
import pytest
# autouse=True, 當前所有測試用例前調用一次
@pytest.fixture(autouse=True)
def foo():
print('foo')
def test_1():
print('普通測試用例111111')
def test_2():
print('普通測試用例22222')
class TestClass(object):
def test_one(self):
print('類實例方法測試用例111111')
def test_two(self):
print('類實例方法測試用例22222')
7. pytest.mark標記
7.1【知道】pytest.mark.xfail()
# 寫了condition后,需要寫上reason
# @pytest.mark.xfail(condition=True, reason='標志失效')
# @pytest.mark.xfail(condition=False, reason='標志失效') # False,相當於沒有寫這句話
# @pytest.mark.xfail(condition=True, reason='標志失效', raises=TypeError)
@pytest.mark.xfail(reason='標志失效')
def test_1():
print('\n普通測試用例111111\n')
7.2【知道】pytest.mark.skip()
7.3【知道】pytest.mark.skipif()
# 修飾的測試用例,跳過,不執行
@pytest.mark.skip(reason='跳過,不執行')
def test_1():
print('\n普通測試用例111111\n')
def test_2():
print('\n普通測試用例22222\n')
# True跳過,False執行
@pytest.mark.skipif(condition=False,reason='跳過,不執行')
def test_3():
print('\n普通測試用例333333\n')
7.4【知道】pytest.mark.parametrize()
import pytest
@pytest.mark.parametrize('num', [1, 2, 3, 4])
def test_get_num(num):
print(num)
@pytest.mark.parametrize(['user', 'pwd'],[('mike123', '12345678'), ('hello11', 'hdshghadshg'), ('aaaaa', 'aaaaaddd')])
def test_login(user, pwd):
print(user, pwd)
test_19.py 1 .2 .3 .4 .mike123 12345678 .hello11 hdshghadshg .aaaaa aaaaaddd
一、pytest(下)1. pytest配置文件1.1【知道】配置pytest命令行運行參數1.2【知道】配置文件參數詳解2. pytest常用插件2.1【掌握】生成測試報告2.2【知道】控制函數執行順序插件2.3 失敗重試2.4 按名稱取消插件3. Yaml3.1【知道】Yaml語法格式3.2 Python處理yaml文件3.2.1【掌握】讀取yaml文件3.2.2【掌握】寫入文件內容4. 綜合案例4.1【理解】測試目錄結構4.2【掌握】公用方法類:讀取yaml數據4.3【掌握】公用方法類:請求封裝4.4【掌握】注冊單元測試用例4.5【掌握】登陸單元測試用例
一、pytest(下)
1. pytest配置文件
1.1【知道】配置pytest命令行運行參數
-
addopts:指定運行的文件,如果配置文件指定了運行的文件,代碼中不建議指定運行文件
-
如果目錄中嵌套背的代碼目錄,這個目錄建議新建一個
__init__.py文件
[pytest]
;addopts = -s test_18.py test_19.py ;注釋
addopts = -s
[pytest]
;addopts = -s test_18.py test_19.py ;注釋,占一行
addopts = -s
;識別目錄 tests
testpaths = ./tests
python_files = test*.py
python_classes = Test*
python_functions = test*
具體參數解讀
-
addopts - 配置pytest命令行運行參數
-
空格分隔,可添加多個命令行參數
-
-
testpaths - 配置測試搜索的路徑
-
當前目錄下的測試腳本文件夾 可自定義
-
-
python_files - 配置測試搜索的文件名
-
當前目錄下的測試腳本文件夾下,以
test_開頭,以.py結尾的所有文件 可自定義
-
-
python_classes - 配置測試搜索的測試類名
-
當前目錄下的測試腳本文件夾下,以
Test_開頭的類 可自定義
-
-
配置測試搜索的測試函數名
-
當前目錄下的測試腳本文件夾下,以
test_開頭的⽅法 可自定義
-
2. pytest常用插件
2.1【掌握】生成測試報告
-
需要先按照插件:
pip install pytest-html # 生成測試報告 -
運行命令:
pytest -s 測試用例文件 --html=./報告名字.html
2.2【知道】控制函數執行順序插件
-
安裝插件:
pip install pytest-ordering -
order=1, 數字小的先運行
import pytest
class Test_ABC:
def setup_class(self):
print("setup_class")
def teardown_class(self):
print("teardown_class")
2.3 失敗重試
-
pip install pytest-rerunfailures -
命令:
--reruns 重來次數 -
失敗重試,重新開始,只重試測試用例失敗的例子
2.4 按名稱取消插件
-
-p no:ordering -p no:html -p no:rerunfailures
3. Yaml
3.1【知道】Yaml語法格式

3.2 Python處理yaml文件
-
pip3 install -U PyYAML
3.2.1【掌握】讀取yaml文件
import yaml
def test_read():
# 1. 打開文件, 文件對象
# 2. 返回數據 = yaml.load(文件對象)
with open('./xxx.yaml', 'r') as f:
data = yaml.load(f, Loader=yaml.FullLoader)
print(data)
3.2.2【掌握】寫入文件內容
import yaml
data={'Search_Data': {
'search_test_002': {'expect': {'value': '你好'}, 'value': '你好'},
'search_test_001': {'expect': [4, 5, 6], 'value': 456}
}
}
# 1. 只寫方式打開文件,制定utf-8編碼,返回文件對象
# 2. yaml.dump(數據,文件對象, allow_unicode=True)
def test_write():
with open('yyy.yaml', 'w', encoding='utf-8') as f:
yaml.dump(data, f, allow_unicode = True)
4. 綜合案例
4.1【理解】測試目錄結構
測試目錄結構:

數據准備:
pytest.ini:
[pytest]
addopts = -s --html=report/report.html
testpaths = ./tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
server.yaml:
schema
register.ymal:
url
login.yaml:
url: '/login'
user1:
username: 'admin11'
password: '12345678'
user2:
username: 'admin22'
password: '12345678'
user3:
username: 'admin33'
password: '12345678'
login_logout.yaml:
url: '/login'
user1:
username: 'admin11'
password: '12345678'
4.2【掌握】公用方法類:讀取yaml數據
# 1. 封裝接口,傳入文件名,自動返回讀取到的文件信息
# 2. 內部處理路徑
import os
import yaml
class Data(object):
# 類屬性,獲取project的絕對路徑
# os.path.dirname()返回去掉文件名后返回的絕對路徑
# os.path.abspath(__file__) 當前文件的絕對路徑
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@classmethod
def read_yaml(cls, file_name):
# 拼接路徑 /home/python/code/pytest_project
file_path = os.path.join(cls.BASE_DIR, 'data', file_name)
print(file_path)
# 打開文件
with open(file_path, 'r') as f:
# 讀取內容
data = yaml.load(f, Loader=yaml.FullLoader)
return data
# print(Data.BASE_DIR)
# print(Data.read_yaml('server.yaml'))
# print(Data.read_yaml('users/register.yaml'))
4.3【掌握】公用方法類:請求封裝
"""
1. 獲取服務器的信息,讀取文件,拆包
2. 封裝get請求,post請求
"""
# 從哪里運行,從哪里開始導包
from pytest_project.utils.data import Data
import requests
class Ask(object):
@staticmethod
def get_url():
"""http://locast:8080"""
# { schema: 'http', host: 'localhost', port: 8080 }
server = Data.read_yaml('server.yaml')
return f"{server['schema']}://{server['host']}:{server['port']}"
@classmethod
def get(cls, path):
"""
:param path: 路徑參數, /register
:return: 響應對象
"""
resp = requests.get(cls.get_url()+path)
return resp
@classmethod
def post(cls, path, data=None, json=None):
"""
:param path: 路徑參數, /register
:return: 響應對象
"""
resp = requests.post(cls.get_url()+path, data=data, json=json)
return resp
4.4【掌握】注冊單元測試用例
import pytest
from pytest_project.utils.ask import Ask
from pytest_project.utils.data import Data
"""
1. 讀取配置文件的信息,提取用戶信息
2. 組包 [(url, {注冊信息}), (url, {注冊信息}), (url, {注冊信息})]
[('/register', {注冊信息1}), ('/register', {注冊信息2}), ('/register', {注冊信息3})]
"""
def get_regiter_info():
users_info = Data.read_user_yaml('register.yaml')
# print(users_info)
args_list = []
for k, v in users_info.items():
if k != 'url':
args_list.append((users_info['url'], v))
# print(args_list)
return args_list
class TestUsers(object):
@pytest.mark.skipif(condition=True, reason='注冊跳過哦')
@pytest.mark.parametrize(['url', 'user_info'], get_regiter_info())
def test_register(self, url, user_info):
resp = Ask.post(url, data=user_info)
print(resp.json())
assert resp.status_code == 200, '注冊失敗'
4.5【掌握】登陸單元測試用例
import pytest
from pytest_project.utils.ask import Ask
from pytest_project.utils.data import Data
"""
1. 讀取配置文件的信息,提取用戶信息
2. 組包 [(url, {注冊信息}), (url, {注冊信息}), (url, {注冊信息})]
[('/register', {注冊信息1}), ('/register', {注冊信息2}), ('/register', {注冊信息3})]
"""
def get_register_info():
users_info = Data.read_user_yaml('register.yaml')
# print(users_info)
args_list = []
for k, v in users_info.items():
if k != 'url':
args_list.append((users_info['url'], v))
# print(args_list)
return args_list
def get_login_info():
users_info = Data.read_user_yaml('login.yaml')
# print(users_info)
args_list = []
for k, v in users_info.items():
if k != 'url':
args_list.append((users_info['url'], v))
# print(args_list)
return args_list
class TestUsers(object):
@pytest.mark.skipif(condition=True, reason='注冊跳過哦')
@pytest.mark.parametrize(['url', 'user_info'], get_register_info())
def test_register(self, url, user_info):
resp = Ask.post(url, data=user_info)
print(resp.json())
assert resp.status_code == 200, '注冊失敗'
@pytest.mark.skipif(condition=False, reason='登陸跳過哦')
@pytest.mark.parametrize(['url', 'user_info'], get_login_info())
def test_login(self, url, user_info):
resp = Ask.post(url, data=user_info)
print(resp.json())
assert resp.status_code == 200, '登陸失敗'
