pytest知識點

一、單元測試框架
1.什么是單元測試框架
單元測試框架是在自動化測試或者白盒測試中對軟件的最小單元(函數(shù)、方法)進行測試的框架
2.單元測試框架分類
Python:unittest,pytest(主流)
Java:Testing(主流)和Junit
3.單元測試框架主要做什么
發(fā)現(xiàn)測試用例
執(zhí)行測試用例
判斷測試結(jié)果
生成測試報告
4.集成度對比
Pytest>UnitTest>RobotFramework>airtest

二、Pytest間接以及常用插件安裝
1.pytest是一個非常成熟的單元測試框架,靈活和簡單,安裝命令:pip install pytest
2.可以結(jié)合selenium,requests,appium完成各種不同的自動化
3.可以生成自定義allure報告以及和Jenkins持續(xù)集成
4.pytest有很多強大的插件
pytest
pytest-html(生成html報告的插件)
pytest-xdist(多線程運行的插件)
pytest-ordering(改變用例的執(zhí)行順序的插件)
pytest-rerunfailures(失敗用例重跑的插件)
allure-pytest(生成美觀自定義的allure報告)
通過在項目的根目錄下新建一個:requirements.txt文件保存插件,通過命令安裝:pip install -r requirements.txt

三、pytest默認測試用例的規(guī)則以及基礎應用
1.模塊名必須以test_開頭或者test結(jié)尾
2.測試類必須以Test開頭,并且不能帶有init方法
3.測試用例必須以test
開頭
4.斷言使用基本的assert即可

import pytest

class TestCase:
    def test_01(self):
        print('第一條用例')
        assert 1 == 1

    def test_02(self):
        print('第二條用例')
        assert 2 == 3

執(zhí)行:
1).通過命令行方法執(zhí)行

# 執(zhí)行所有用例
pytest
# 執(zhí)行某個用例文件
pytest test_case.py
# 執(zhí)行某用例中某方法
pytest test_case.py::test_01

執(zhí)行的參數(shù):
-v 輸出詳細信息,如:pytest -v
-s 輸出調(diào)試信息
-rA 測試結(jié)果的簡單統(tǒng)計
-n 多線程運行,如:pytest -vs -n=2
--reruns num 失敗重跑(前提安裝插件:pytest-rerunfailures)
raise Exception() 拋出異常
try except 解決異常
-x 出現(xiàn)一個用例失敗則停止測試 ,如:pytest -vs -x
---maxfail 出現(xiàn)幾個失敗才終止,如:pytest -vs --maxfail=2
--html 生成html的報告(前提安裝插件:pytest-html),如:pytest -vs --html ./report/report.html
-k 運行測試用例名稱中包含某個字符串的測試用例, 如:pytest -vs -k "name"

2).通過主函數(shù)main()方法執(zhí)行

if __name__ == '__main__':
    pytest.main()

添加參數(shù):pytest.main(["-vs"])
3).通過全局配置文件pytest.ini文件執(zhí)行
注意:
a.一般放在項目的根目錄,名稱必須是pytest.ini,當有中文時可能需要改變編碼格式為GB2312
b.pytest.ini文件可以改變默認的測試用例規(guī)則
不管是命令行運行還是主函數(shù)運行,都會加載這個配置文件
c.Pytest默認尋找當前路徑下所有的文件與子文件中以test開頭的文件夾、文件、函數(shù)作為識別對象
d.Pytest默認不輸出任何打印信息,如果需要看打印信息,要在運行時添加-s指令,如:pytest.main(['-s'])
pytest.ini

[pytest]
addopts = -vs -m "smoke"
testpaths = ./testcases
python_files = test_*.py  # 測試test_開頭的文件
python_classes = Test*  # 測試Test開頭的類
python_functions = test_* # 測試test_開頭的方法
# 測試用例標記分組執(zhí)行
markers = 
    smoke:冒煙用例
    product_manage:商品管理 

這里的-m "smoke"表示只執(zhí)行冒煙用例

# 標記分組執(zhí)行
import pytest

class TestCase:
    # 標記分組執(zhí)行
    @pytest.mark.smoke
    def test_01(self):
        print("測試pytest案例01")

    def test_02(self):
        print("測試pytest案例02")

四、pytest跳過測試用例
1.無條件跳過

@pytest.mark.skip(reason="無理由跳過")

2.有條件跳過

@pytest.mark.skipif(reason="工作經(jīng)驗少于10年跳過")
# pytest跳過測試用例
import pytest


class TestCase:
    # 無條件跳過
    @pytest.mark.skip(reason="無理由跳過")
    # 有條件跳過
    # @pytest.mark.skip(reason="工作經(jīng)驗不少于10年")
    def test_01(self):
        print("測試pytest案例01")

    def test_02(self):
        print("測試pytest案例02")

五、pytest測試用例的前后置固件
common_util.py

class CommonUtil:
    def setup_class(self):
        print("每個類用例之前執(zhí)行一次")

    def teardown_class(self):
        print("每個類用例之后執(zhí)行一次")

    def setup(self):
        print("每個用例之前執(zhí)行一次")

    def teardown(self):
        print("每個用例之后執(zhí)行一次")

test_case.py

import pytest
from common.common_util import CommonUtil

class TestCase(CommonUtil):

    def test_01(self):
        print("測試pytest案例01")

    def test_02(self,):
        print("測試pytest案例02")

六、使用fixture實現(xiàn)部分前后置

@pytest.fixture(scope="function",autouse=False,params=None,ids=None,name=None)

1.scope:作用域
function: 在函數(shù)之前和之后執(zhí)行
1).手動調(diào)用的方式是在測試用例的參數(shù)里加入fixture的名稱

# 使用fixture實現(xiàn)部分前后置

import pytest


@pytest.fixture(scope="function")
def exe_database_sql():
    print("執(zhí)行sql查詢")


class TestCase():

    @pytest.mark.skip(reason="無理由跳過")
    def test_01(self):
        print("測試pytest案例01")

    # 手動執(zhí)行方法fixture
    def test_02(self, exe_database_sql):
        print("測試pytest案例02")

2).如果說fixture通過return或yield返回值的話,可以把這個值傳遞到測試用例當中,并且值是通過固件的名字傳遞的

# 使用fixture實現(xiàn)部分前后置
import pytest


# 自動執(zhí)行方法fixture
@pytest.fixture(scope="class", autouse=True)
def exe_database_sql():
    print("執(zhí)行sql查詢")
    yield "success"
    print("關閉數(shù)據(jù)庫鏈接")


class TestCase():

    def test_01(self):
        print("測試pytest案例01")

    def test_02(self, exe_database_sql):
        print("測試pytest案例02")
        print(exe_database_sql)

class: 在類之前和之后執(zhí)行
1).手動調(diào)用的方式是在類的上面加上@pytest.mark.usefixtures("name")裝飾器調(diào)用

# 使用fixture實現(xiàn)部分前后置
import pytest


@pytest.fixture(scope="class", autouse=False)
def exe_database_sql():
    print("執(zhí)行sql查詢")
    yield "success"
    print("關閉數(shù)據(jù)庫鏈接")


# 手動執(zhí)行類fixture
@pytest.mark.usefixtures("exe_database_sql")
class TestCase(CommonUtil):

    def test_01(self):
        print("測試pytest案例01")

    def test_02(self):
        print("測試pytest案例02")
        print(exe_database_sql)

package/session: 在整個項目會話之前和之后執(zhí)行
1).一般會結(jié)合conftest.py文件來實現(xiàn)
module:在模塊級別中只執(zhí)行一次

2.autouse: 自動執(zhí)行,默認是Flase
如果希望在另外一個py文件中調(diào)用需要結(jié)合conftest.py文件使用

3.params:實現(xiàn)參數(shù)化
1.如何把值傳到fixture是通過在fixture函數(shù)的參數(shù)里加入request來接受參數(shù),然后通過request.param來取值(此處param沒有s)

# param實現(xiàn)參數(shù)化
import pytest


# 使讀取數(shù)據(jù)方法
def read_yaml():
    return ['成龍', '甄子丹', '李連杰']


@pytest.fixture(scope="function", autouse=False, params=read_yaml())
def exe_database_sql(request):
    print(request.param)
    print("執(zhí)行sql查詢")
    yield request.param
    print("關閉數(shù)據(jù)庫鏈接")


class TestCase:

    def test_01(self):
        print("測試pytest案例01")

    def test_02(self, exe_database_sql):
        print("測試pytest案例02")
        print(exe_database_sql)

4.ids:不能單獨使用,必須和params一起使用,作用是對參數(shù)起別名

# ids對參數(shù)起別名
import pytest

# 使讀取數(shù)據(jù)方法
def read_yaml():
    return ['成龍', '甄子丹', '李連杰']

@pytest.fixture(scope="function", autouse=False, params=read_yaml(), ids=['chenglong', 'zenzidan', 'lilianjie'])
def exe_database_sql(request):
    print("執(zhí)行sql查詢")
    yield request.param
    print("關閉數(shù)據(jù)庫鏈接")

class TestCase:
    def test_01(self):
        print("測試pytest案例01")

    def test_02(self, exe_database_sql):
        print("測試pytest案例02")
        print(exe_database_sql)

5.name:作用是給fixture起別名
特別注意:一旦使用了別名,fixture名稱就不能用,只能用別名name

# name對fixture起別名
import pytest

# 使讀取數(shù)據(jù)方法
def read_yaml():
    return ['成龍', '甄子丹', '李連杰']

@pytest.fixture(scope="function", autouse=False, params=read_yaml(), ids=['chenglong', 'zenzidan', 'lilianjie'], name="db")
def exe_database_sql(request):
    print("執(zhí)行sql查詢")
    yield request.param
    print("關閉數(shù)據(jù)庫鏈接")

class TestCase:

    def test_01(self):
        print("測試pytest案例01")

    def test_02(self, db):
        print("測試pytest案例02")
        print(db)

用例順序按照指定方式:@pytest.mark.run(order=)
指定用例運行:@pytest.mark.任意名字,執(zhí)行時添加‘-m 任意名字’

import pytest
class TestCase:
    @pytest.mark.smoke
    def test_01(self):
        print("測試pytest案例01")
  
    @pytest.mark.run(order=1)
    def test_02(self):
        print('測試pytest案例02’)
if __name__ = '__main__':
    pytest.main(['s', '-v', '-m', 'smoke'])

七、fixture結(jié)合conftest.py文件使用
1.conftest.py是專門用于存放fixture的配置文件,名稱是固定的不能變
conftest.py

import pytest

# 使讀取數(shù)據(jù)方法
def read_yaml():
    return ['成龍', '甄子丹', '李連杰']

@pytest.fixture(scope="function", autouse=False, params=read_yaml(), ids=['chenglong', 'zenzidan', 'lilianjie'], name="db")
def exe_database_sql(request):
    print("執(zhí)行sql查詢")
    yield request.param
    print("關閉數(shù)據(jù)庫鏈接")

test_case.py

# 直接使用固件fixture的別名db
class TestCase:

    def test_01(self):
        print("測試pytest案例01")

    def test_02(self, db):
        print("測試pytest案例02")
        print(db)

2.在conftest.py文件里所有的方法在使用時不需要打包導包
3.conftest.py文件可以有多個,并且多個conftest.py文件里面的多個fixture可以被一個用例調(diào)用

八、setup、teardown、setup_class、teardown_class、fixture、conftest優(yōu)先級
會話:fixture的session級別優(yōu)先級最高,setup_session

類:fixture的class級別優(yōu)先級最高
setup_class > setup method > setup > teardown > teardown method > teardown class
類:setup_class

函數(shù):fixture的function級別優(yōu)先級最高,函數(shù):setup_function

九、總結(jié)pytest執(zhí)行過程
1.查詢當前目錄下的conftest.py文件
2.查詢當前目錄下的pytest.ini文件,找到測試用例的位置
3.查詢用例目錄下的conftest.py文件
4.查詢測試用例的py文件中是否有setup、teardown、setup_class、teardown_class
5.再根據(jù)pytest.ini文件的測試用例的規(guī)則去查找用例并執(zhí)行

十、pytest斷言
就是使用pytest自己的斷言assert

1.assert flag is True
2.assert 1 == 1
3.assert 'a' in 'abc'
# pytest斷言assert
class TestCase:
    def test_01(self):
        print("測試pytest案例01")
        assert 1 > 2

    def test_02(self, db):
        print("測試pytest案例02")
        print(db)
        assert 1 >= 1

十一、pytest結(jié)合allure-pytest插件生成美觀的報告
1.安裝allure-pytest插件
2.下載allure,下載解壓之后,還需配置環(huán)境變量(把allure目錄下的bin目錄配置到系統(tǒng)變量的path路徑),下載地址:https://github.com/allure-framework/allure2/releases
驗證allure是否安裝成功:allure --version
1).先在dos窗口驗證

allure --version

2).在pycharm驗證(如果驗證失敗,重啟pycharm)
3.生成allure報告
1)生成臨時的json報告,在pytest.ini文件加入以下內(nèi)容:

addopts = -vs --alluredir=./temps --clean-alluredir

--alluredir=./temps 生成臨時報告
--clean-alluredir 清空臨時報告
2)生成正式的allure報告
run.py

import pytest
import os
import time

if __name__ == '__main__':
    pytest.main()
    time.sleep(3)
    os.system("allure generate ./temps -o ../report --clean")

3)定制化
注意:安裝的allure-pytest和allure版本需相匹配

十二、pytest之parametrize()實現(xiàn)數(shù)據(jù)驅(qū)動
方法:@pytest.mark.parametrize(args_name,args_value)
args_name:參數(shù)名稱,用于將參數(shù)值傳遞給函數(shù)
args_vlaue:參數(shù)值(列表和字典列表,元祖和字典元祖)有n個值用例執(zhí)行n次
第一種用法:

# pytest之parametrize()實現(xiàn)數(shù)據(jù)驅(qū)動
import pytest

class TestCase:
    @pytest.mark.parametrize('caseinfo', ['chenglong', 'zenzidan', 'lilianjie'])
    def test_01(self, caseinfo):
        print("測試pytest案例01")
        print(caseinfo)

第二種用法:

# pytest之parametrize()實現(xiàn)數(shù)據(jù)驅(qū)動
import pytest

class TestCase:
    @pytest.mark.parametrize('arg1, arg2', [['chenglong', 'zenzidan'], ['lilianjie', 'xietingfeng']])
    def test_01(self, arg1, arg2):
        print("測試pytest案例01:"+str(arg1)+""+str(arg2))

十三、YAML格式測試用例讀寫,清楚封裝
1.yaml是一種數(shù)據(jù)格式,擴展名可以使yaml,yml,支持#注釋,通過縮進表示層級,區(qū)分大小寫。
用途:
用于做配置文件(yam,ini)
用于編寫自動化測試用例
2.數(shù)據(jù)組成
1)map對象,鍵值對(鍵和值之間有空格)
yaml_util.py

# 讀取yaml,map對象
import yaml
import os

# 獲取項目的根目錄
def get_obj_path():
    # 當前文件的目錄
    # return os.path.dirname(__file__)
    # 項目根目錄
    return os.path.dirname(__file__).split('common')[0]

# 讀取yaml,需要安裝pip install pyyaml
def read_yaml(yamlpath):
    # with open('yaml文件的路徑', mode='r', encoding='utf-8') as f: value = yaml.load(stream=f, Loader=yaml.FullLoader)
    with open(get_obj_path()+yamlpath, mode='r', encoding='utf-8') as f: value = yaml.load(stream=f, Loader=yaml.FullLoader)
    return value

# # 讀取yaml文件,r為讀取文件內(nèi)容
    # files = open(filename, 'r', encoding='utf-8')
    # # 讀取files的內(nèi)容
    # data = yaml.load(files, Loader=yaml.FullLoader)
    # return data

if __name__ == '__main__':
    print(read_yaml('pytest1/get_token.yaml'))

get_token.yaml

name: 'token value'

2)數(shù)組(list)使用'-'表示列表
yaml_util1.py

# 讀取yaml,list數(shù)組
import yaml
import os

# 獲取項目的根目錄
def get_obj_path():
    # 當前文件的目錄
    # return os.path.dirname(__file__)
    # 項目根目錄
    return os.path.dirname(__file__).split('common')[0]

# 讀取yaml,需要安裝pip install pyyaml
def read_yaml(yamlpath):
    # with open('yaml文件的路徑', mode='r', encoding='utf-8') as f: value = yaml.load(stream=f, Loader=yaml.FullLoader)
    with open(get_obj_path()+yamlpath, mode='r', encoding='utf-8') as f: value = yaml.load(stream=f, Loader=yaml.FullLoader)
    return value

if __name__ == '__main__':
  print(read_yaml('pytest1/get_token1.yaml'))

get_token1.yaml

yamlist:
  - name1: '成龍'
  - name2:
      - title1: '李連杰'
      - title2: '謝霆鋒'
  - name3: '甄子丹'

十四、parametrize結(jié)合yaml實現(xiàn)接口自動化測試

# 未封裝request
import requests
import pytest
from common.common_util import CommonUtil
from common.yaml_util import read_yaml


class TestApi(CommonUtil):
    session = requests.session()

    @pytest.mark.parametrize('caseinfo', read_yaml('./pytest1/get_token2.yaml'))
    def test_01_get_token(self, caseinfo):
        print('獲取統(tǒng)一接口鑒權碼')
        print(caseinfo)
        name = caseinfo['name']
        method = caseinfo['request']['method']
        url = caseinfo['request']['url']
        data = caseinfo['request']['data']
        validate = caseinfo['validate']
        res = TestApi.session.request(method=method, url=url, params=data)
        result = res.json()
        print(result)

封裝request
requests_util.py

import requests


class RequestUtil:
    session = requests.session()

    def send_request(self, method, url, data, **kwargs):
        method = str(method).lower()
        if method == "get":
            res = RequestUtil.session.request(method, url, params=data, **kwargs)
        elif method == "post":
            res = RequestUtil.session.request(method, url, json=data, **kwargs)
        return res

test_case.py

# 封裝request
import pytest
from common.common_util import CommonUtil
from common.yaml_util import read_yaml
from common.requests_util import RequestUtil


class TestApi(CommonUtil):

    @pytest.mark.parametrize('caseinfo', read_yaml('./pytest1/get_token2.yaml'))
    def test_01_get_token(self, caseinfo):
        print('獲取統(tǒng)一接口鑒權碼')
        print(caseinfo)
        name = caseinfo['name']
        method = caseinfo['request']['method']
        url = caseinfo['request']['url']
        data = caseinfo['request']['data']
        validate = caseinfo['validate']
        res = RequestUtil.session.request(method=method, url=url, params=data)
        result = res.json()
        print(result)

十五、接口自動化測試框架的封裝
1.yaml文件如何實現(xiàn)接口關聯(lián)封裝
2.yaml文件如何實現(xiàn)動態(tài)參數(shù)的處理
3.yaml文件如何實現(xiàn)文件上傳
4.yaml文件如何實現(xiàn)解決斷言,特別是當有參數(shù)化的時間如何斷言
5.yaml文件數(shù)據(jù)量太大如何處理
6.接口自動化框架的擴展性:加密接口、簽名接口、自定義的功能接口

接口自動化測試框架規(guī)則

1.必須有四個一級關鍵字:name,base_url,rquest,validate
2.在request一級關鍵字下必須包括兩個二級關鍵字:method,url
3.傳參方式:在request一級關鍵字下,通過二級關鍵字傳遞參數(shù)
1)如果是get請求,通過params傳參
2)如果是post請求,傳json格式,通過json關鍵字傳參;傳表單格式,通過data關鍵字傳參
3)如果是文件格式,通過files關鍵字傳參,如:

files: media: "D:\video.png"

4.如果需要做接口關聯(lián),必須使用一級關鍵字:extract,框架支持json提取和正則提取
提?。?br> 1)json提取方式:

extract: access_token: access_token

2)正則表達式方式:

extract:access_token: '"access_token":"(.*?)"'

5.熱加載,當yaml需要使用動態(tài)參數(shù)時,那么可以在debug_talk.py文件中寫方法調(diào)用,如:

# 獲取隨機數(shù)的方法
def get_random_number(self, min, max):
    return random.randint(int(min), int(max))

6.框架支持兩個斷言方式,分別為equals和contains斷言,如:
validate:

  • equals: {status_code: 205}
  • contains: {access_token}
    7.數(shù)據(jù)驅(qū)動使用csv和一級關鍵字parameters實現(xiàn)。
    1)yaml寫法
    parameters:
    name_appid: appidvalue
    2)csv寫法

8.日志監(jiān)控,異常處理以及基礎路徑的設置

十六、YAMLJ簡介
1.用途
1)用于做配置文件,配置全局的數(shù)據(jù):環(huán)境變量、數(shù)據(jù)庫信息、賬號信息、日志格式、日志報告名稱等。
2)用于寫測試用例(接口自動化測試用例),用于數(shù)據(jù)驅(qū)動

2.YAML簡介
yaml是一種數(shù)據(jù)格式,支持注釋、換行、多行字符串、裸字符串等

3.yaml語法規(guī)則
1)大小寫敏感
2)使用縮進表示層級關系
3)縮進不管空格的數(shù)量,只要層級的左邊對齊就行
4)#表示注釋

4.yaml和json數(shù)據(jù)結(jié)構對比
JSON:
1)Map對象:鍵值對(字典dict),使用{}括起來,如:

{name: 陳銘}

2)數(shù)組(列表list),使用[]括起來,如:

[{name: 陳銘}]

YAML:
1)Map對象:鍵值對(字典dict)

name: 陳銘
#  一行的寫法
list: {name: 陳銘}

2)數(shù)組(列表list),用一組橫行'-'開頭

yamllist:
    - name: 陳銘
# 一行的寫法
yamlist: [{name: 陳銘}]

5.yaml配置文件處理

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容