Pytest進階使用

fixture

特點:

  • 命令靈活:對於setup,teardown可以省略

  • 數據共享:在conftest.py配置里寫方法可以實現數據共享,不需要import導入,可以跨文件共享

  • scope的層次及神奇的yield組合相當於各種setup和teardown

  • 實現參數化

應用

  • 場景:

測試用例執行時,有的用例需要登錄才能執行,有些用例不需要登錄。

setup和teardown無法滿足這種情況,但是fixture可以。默認scope(範圍):function

  • 步驟:

    • 導入pytest

    • 在登錄的函數上添加@pytest.fixture()

    • 在要使用的測試方法中傳入(登錄函數名稱)

    • 不傳入的就不登錄,直接執行測試方法。

fixture作用域

取值 範圍 說明
function 函數級 每一個函數或方法都會調用
class 類級別 每個測試類只執行一次
module 模塊級 每個.py文件調用一次
package 包級 每個python包只調用一次(暫不支持)
session 會話 每次會話只需要運行一次,會話內所有方法及類,模塊都共享這個方法
  • session是在整個項目中只執行一次的代碼

yield關鍵字

  • 場景:

你已經可以將測試方法【前要執行的或依賴的】解決了,那測試方法後銷毀清除數據要如何進行?

  • 解決:

通過在fixture函數中加入yield關鍵字,yield是調用第一次返回結果,第二次執行它下面的語句返回。

  • 步驟:

@pytest.fixture(scope=module)

在登錄的方法中加yield,之後加銷毀清除的步驟

import pytest
 
@pytest.fixture()
def login():
    # setup
    token = '1235236fdg'
    print("登錄功能")
    yield token# 相當於return 返回none
    # teardown
    print("退出登錄操作")
 
def test_search():
    print("搜索功能")
 
def test_cart(login):
    print(f"token:{login}")
    print("購物車")

數據共享

  • 場景:

你與其他工程師合作一起開發時,公共的模塊要在不同文件中,要在大家都訪問的到的地方

  • 解決:

使用conftest.py這個文件進行數據共享,並且它可以放在不同位置起着不同的範圍共享作用

  • 前提:

    • conftest文件名不能換

    • 放在項目下是全局的數據共享

  • 執行:

    • 系統執行到參數login時先從本模塊中查找是否有這個名字的變量之類的

    • 之後在conftest.py中找是否含有

  • 步驟:

將登錄模塊帶@pytest.fixture寫在conftest.py

自動應用

  • 場景:

不想原測試方法有任何改動,或全部都自動實現自動應用,沒特例,也都不需要返回值時可以選擇自動應用的方法

  • 解決:

使用fixture中的參數autouse=True實現

  • 步驟:

在方法上面加@pytest.fixture(autouse=Ture)

參數化

  • 場景:

測試離不開數據,為了數據靈活,一般數據都是通過參數傳的

  • 解決:

使用fixture中的固定參數request傳遞

  • 步驟:

在fixture中添加@pytest.fixture(params=[1,2,3,’linda’])

在方法參數寫request,方法體裏面使用request.param接受參數

@pytest.fixture(params=['hogwarts','joker'])
def demo_params(request):
    print(f'用戶名為:{request.param}')
    return request.param


def test_demo(demo_params):
    print(f"數據為:{demo_params}")
  • 注意:fixture的參數是params,而調用的時候是request.param,沒有s

總結:

  • 模擬setup,teardown(一個用例可以引用多個fixture)

  • yield的用法

  • 作用域(session,module,類級別,方法級別)

  • 自動執行(autouse參數)

  • conftest.py用法,一般會把fixture寫在conftest.py文件中

  • 實現參數化

pytest.ini文件

  • pytest.ini是pytest的配置文件

  • 可以修改pytest的默認行為

  • 不能使用中文符號,包括漢字,空格 ,引號,冒號等

作用:

  • 修改用例的命名規則

  • 配置日誌格式,比代碼配置方便很多

  • 添加標籤,防止運行過程報警告錯誤

  • 指定執行目錄

  • 排除搜索目錄

改變pytest運行規則

[pytest]
;執行check_開頭的所有文件
python_files = check_* test_*
;執行所有的以Test和Check開頭的類
python_classes = Test* Check*
;執行所有以test_和check_開頭的方法
python_functions = check_* test_*
  • 注意:win系統的pytest.ini文件不能寫中文,注釋也不行

pytest配置-添加默認參數

addopts = -v -s –alluredir=./results

指定/忽略執行目錄

;設置執行得路徑
;testpaths = bilibili baidu
;忽略某些文件夾/目錄
norecursedirs = result logs datas test_demo*

插件開發

  • pytest插件分類

    • 外部插件:pip install 安裝的插件

    • 本地插件:pytest自動模塊發現機制(conftest.py存放的)

    • 內置插件:代碼內部的_pytest目錄加載(hook函數)

官網://pypi.org/

常用插件

每一種測試框架收集測試用例的順序是不一樣的

pytest執行順序控制

  • 場景:

對於集成測試,經常會有上下文依賴關係的測試用例。如十個步驟,拆分成十個case,這時候能知道到底執行到哪步報錯。

用例默認執行順序:自上而下執行

  • 解決:

可以通過setup,teardown和fixture來解決,也可以使用pytest-ordering插件來解決

  • 安裝:pip install pytest-ordering

  • 用法:@pytest.mark.run(order=2)

  • 注意:多個插件裝飾器(>2)的時候,有可能會發生衝突

並行與分佈式並發執行(xdist)

場景1:

  • 測試用例1000條,一個用例執行1分鐘,一個測試人員需要1000分鐘,通常我們會用人力成本換取時間成本,加幾個人一起執行,時間就會縮短。這就是一種分佈式場景。

場景2:

  • 假設有個報名系統,對報名總數進行統計,數據同時進行修改操作的時候有可能出現問題,需要模擬這個場景,需要多用戶並發請求數據

解決:

  • 使用分佈式並發執行測試用例,分佈式插件:pytest-xdist

  • 安裝:pip install pytest-xdist

  • 注意:用例多的時候效果明顯,多進程並發執行,同時支持allure

hook函數

1. 介紹

  • 是個函數,在系統消息觸發時被系統調用

  • 自動觸發機制

  • Hook函數的名稱是確定的

  • pytest有非常多的hook函數

  • 使用時直接編寫函數體

  • 執行是有先後順序的

  • 可以在不同階段實現不同的功能

pytest執行過程

執行順序:

pytest編寫插件1-修改默認編碼

pytest_collection_modifyitems收集上來的測試用例實現定製化功能

解決問題:

  • 自定義用例的執行順序

  • 解決編碼問題(中文的測試用例名稱)

  • 自動添加標籤

from typing import List


# 修改編碼的hook函數
def pytest_collection_modifyitems(
    session: "Session", config: "Config", items: List["Item"]
) -> None:
    # items里的name是測試用例的名字,nodeid是測試用例的路徑
    print(items)
    for item in items:
        # 如果想改變unicode編碼格式的話,需要先encode成utf-8格式的,再decode成unicode-escape就可以了
        item.name = item.name.encode('utf-8').decode('unicode-escape')
        item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')

編寫插件2-添加命令行參數

# 定義命令行參數的hook函數
def pytest_addoption(parser):
    # group 將下面所有的option都展示在這個group組下
    mygroup = parser.getgroup('hogwarts')
    mygroup.addoption('--env',  # 註冊一個命令行選項
                      default='test',  # 參數的默認值
                      dest='env',  # 存儲的變量,為屬性命令,可以使用option對象訪問到這個值
                      help='set your run env')  # 幫助提示,參數的描述信息


@pytest.fixture(scope='session')
def cmd_option(request):
    # request獲取命令行的參數,config拿到pytest相關配置,getoption拿到命令行參數
    return request.config.getoption('--env')

打包發佈

打包項目構成:

  • 源碼包

  • setup.py

  • 測試包

from setuptools import setup, find_packages

setup(
    name='pytest_encode',
    url='',
    version='1.0',  # 版本
    author='joker',  # 作者
    author_email='',  # 郵箱
    description='set your encoding and logger',  # 描述用法
    long_description='Show Chinese for you mark.parametrize().',  # 完整描述
    classifiers=[  # 分類索引,pip所屬包的分類,方便在pip官網中搜索
        'Framework :: Pytest',
        'Programming Language :: Python',
        'Topic :: Software Development :: Testing',
        'Programming Language :: Python :: 3.8',

    ],
    license='proprietary',  # 程序授權信息
    packages=find_packages(),  # 通過導入的方式發現當前項目下所有的包
    keywords=[  # 便於pip進行分類
        'pytest', 'py.test', 'pytest_encode'
    ],
    # 需要安裝的依賴
    install_requires=[
        'pytest'
    ],
    # 入口模塊,或者入口函數(最重要的)
    entry_points={
        'pytest11': [
            'pytest_encode = pytest_encode.main'
        ]
    },
    zip_safe=False
    # 針對win系統,不設置成false會出錯
)

打包命令

依賴包安裝:

  • pip install setuptools python的包管理工具,負責安裝和發佈,尤其是安裝擁有依賴關係的包

  • pip install wheel 生成 *.whl格式的安裝包,本質上也是一個壓縮包

打包命令:(切到setup.py所在的目錄下執行)

python setup.py sdist bdist_wheel

dist目錄下.whl的文件,可以通過pip install 下載

發佈命令

  • python3 -m pip install –user –upgrade twine ## 安裝twine工具

  • python3 -m twine upload –repository testpypi dist/* ## 上傳代碼