Pytest自動化框架


一、Pytest介紹

官網:https://docs.pytest.org/en/7.1.x/

——Pytest是一個測試用例的管理框架,在Unitest基礎上做的一個全面的升級.

  1. 集成度更高,而且更加靈活的一個測試框架(測試用例開頭不想使用test打頭,可進行自定義)
  2. 運行的順序是自上而下,不是像Unitest是根據字母來進行執行
  3. 全程都是基於指令的形式來運行(通過指令就可以直接生成測試報告)
  4. 可以直接使用Unitest
  5. Pytest默認規則是:尋找當前路徑下所有的文件與子文件中以test開頭或是結尾的文件夾、文件、函數作為識別對象,所以我們在創建Python模塊時以及命名函數時,都需要是【test開頭或是結尾】,不然則不會被識別到
  6. Pytest默認不輸出任何打印信息,如果要看打印信息,需要在運行時添加 “  -s  ” 的指令
  7. 多條指令一同運行時,需要通過 空格 進行區分,在 main 函數中,是通過 逗號 來分割
  8.  -v 用於詳細顯示日志信息是否通過
  9.  -rA 測試結果的簡單統計
  10. -q 只顯示運行結果以及時間
  11. 預置函數: 一般可以通過一個配置文件直接進行管理,配置文件命名一定要是 conftest.py(不能是其他的)用於前期的數據准備,如:可把獲取token放入到預置函數中
  12. fixture是pytest中的一大利器

二、Pytest環境部署

pip install pytest

 三、操作使用

import pytest


def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main()       #不會打印出信息
    pytest.main(['-s']) #可以直接打印出信息

結果:

 我們可以從結果中看出,並沒有打印出信息,如果相關查看打印信息,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s運行

  • 多個test文件、函數,指定自定義運行

(1)運行指定的 .py模塊,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s test_case1.py

 

結果:

(2)運行 .py模塊下某個函數,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s test_case1.py::test_002

 結果:

方法二:代碼中指定:pytest.main(['-s','test_case1.py::test_002'])

(3)指定 .py模塊下例運行哪幾個,以及指定運行順序,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s test_case1.py::test_002 test_case1.py::test_001

 (4)"-V "  打印出用例是否通過結果

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-v']) # "-V"會打印出用例是否通過結果

 結果:

(5)mian中命令,可放在一起寫,如:

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-sv']) 

 結果:

 (6) -rA 測試結果的簡單統計

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-rA'])

結果:

 

 (7)-q 只顯示運行結果以及時間

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-q'])

 結果:

 

 四、配置文件 conftest.py 中 fixture 的使用:

(1)基本使用

  • conftest.py

import pytest

#預置函數
@pytest.fixture() #裝飾器
def keep():
    print('堅持下去!!!')
  • test_case.py中執行代碼

import pytest

def test_01(keep):
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-s'])

結果:

 我們可以看出,是相當於把 conftest.py 中的預置函數keep傳給了 test_case.py中的 test_01函數,執行代碼時,首先運行的conftest.py 中的預置函數keep,然后在依次執行的test_case.py中的2個函數。

 (2)斷言操作

  • conftest.py

import pytest

@pytest.fixture()
def keep01():
    return 1
  • 在test_case.py中執行代碼

import pytest

def test_01(keep01):
    assert keep01 == 1,'失敗'
    assert keep01 == 2, '失敗'


if __name__ == "__main__":
    pytest.main(['-s'])

結果:

 

 (3)fixture中scope參數定義的4種等級(默認等級是funtion):

  1. session:在本次session級別中只執行一次(說的是運行結果中標題顯示的:==test session starts==)
  2. module: 在模塊級別中只執行一次,就是說在第一個模塊運行一次就可以了,之后的模塊在使用,直接取就可以了
  3. class:在類級別中執行一次
  4. funtion:在函數級別中執行,每有一個函數就執行一次
  • conftest.py

import pytest

@pytest.fixture(scope='session') #字符串中還可以是:module、class、funtion
def keep01():
    return 1

五、前置、后置條件

  • setup_functionteardown_function

import pytest

#前置、后置條件
def setup_function():
    print('===setup_function===')

def teardown_function():
    print('***teardown_function***')

def test_01(keep01):
    print('test_01')

if __name__ == "__main__":
    pytest.main(['-s'])

 結果:

 

  • setup_moduleteardown_module

import pytest

#前置、后置條件,函數名字是固定寫法,不可以自定義 
def setup_function():
    print('===setup_function===')

def teardown_function():
    print('***teardown_function***')

def setup_module():
    print('。。。setup_module。。。')

def teardown_module():
    print('+++teardown_module+++')

def test_01(keep01):
    print('test_01')

if __name__ == "__main__":
    pytest.main(['-s'])

結果:

 六、類的使用

import pytest

#前置、后置條件
def setup_function():
    print('===setup_function===')

def teardown_function():
    print('***teardown_function***')

def setup_module():
    print('。。。setup_module。。。')

def teardown_module():
    print('+++teardown_module+++')
    
#pytest中class對象的定義:建議以test開頭,不需要繼承了
class TestDemo:
    def test_d1(self):
        print('類===test_d1===')

    def test_d2(self):
        print('類***test_d2***')


def test_01(keep01):
    print('普通函數test_01')

if __name__ == "__main__":
    pytest.main(['-s'])

結果:

*  我們從結果可以看出setup_function晚於類執行,在類里面是沒有執行setup_function的。也就是說在類外邊定義的setup_function,只能在類外邊起作用,在類里面不起作用。

  • 在類里面使用setupteardown

import pytest

class TestDemo:
    def setup(self):
        print('class setup')

    def teardown(self):
        print('class teardown')

    def test_d1(self):
        print('類===test_d1===')

    def test_d2(self):
        print('類***test_d2***')

if __name__ == "__main__":
    pytest.main(['-s'])

結果:

  •  在類里面使用setupteardown、setup_classteardown_class、setup_metchodteardown_metchod

import pytest

#前置、后置條件
class TestDemo:
    def test_001(self):
        print('類:test_001')
    def setup(self):
        print('類:setup')

    def teardown(self):
        print('類:teardown')

    def setup_class(self):
        print('類===setup_class===')

    def teardown_class(self):
        print('類***teardown_class***')

    def setup_method(self):
        print('類===setup_method===')

    def teardown_method(self):
        print('類***teardown_method***')

if __name__ == "__main__":
    pytest.main(['-s'])

 結果:

  •  在class中 前置、后置 函數的運行順序:
  1. setup class
  2. setup method
  3. setup
  4. teardown
  5. teardown methon
  6. teardown class

七、pytest中用例的管理手段:mark

——可以通俗的理解為:是一個批量管理工具,標記好了,就可以批量的運行標記為一樣的用例了。

  • 可以通過mark裝飾器對所有的用例進行標記,不同的標記區分進行管理
  • 在一個py文件還是多個py文件都會生效

(1)運行一種被標記的用例

import pytest

@pytest.mark.webui
def test_01():
    print('web01')

@pytest.mark.webui
def test_02():
    print('web02')

@pytest.mark.app
def test_03():
    print('app01')

@pytest.mark.app
def test_04():
    print('app02')

if __name__ == "__main__":
    pytest.main(['-s','test_case2.py','-m webui']) # -m webui:指定運行是webui的用例

結果:

(2)用例被@pytest.mark標記多個時,運行其中一種被標記的用例

import pytest

@pytest.mark.webui
def test_01():
    print('web01')

@pytest.mark.webui
def test_02():
    print('web02')

@pytest.mark.app @pytest.mark.temp def test_03():
    print('app01')

@pytest.mark.app @pytest.mark.temp def test_04():
    print('app02')

if __name__ == "__main__":
    pytest.main(['-s','test_case2.py','-m temp']) # -m webui:指定運行是webui的用例

 結果:

 

 (2)同時運行多個不同的被標記種類,-m name1,neme2

pytest.main(['-s','test_case2.py','-m app,webui']) 

八、運行方式:

  1、命令行模式

通過標記表達式執行
pytest -m demo
這條命令會執行被裝飾器@pytest.mark.demo裝飾的所有測試用例

生成html報告:
pytest -m demo --html=Report/report.html

生成xml報告:
pytest -m demo --junitxml=Report/report.xml

運行指定模塊:
pytest -m demo --html=Report/report.html TestCases/test_pytest.py

運行指定測試目錄
pytest -m demo --html=Report/report.html TestCases/

通過節點id來運行:
pytest TestCases/test_pytest.py::TestDemo::test_demo01

通過關鍵字表達式過濾執行
pytest -k "MyClass and not method"
這條命令會匹配文件名、類名、方法名匹配表達式的用例

獲取用例執行性能數據
獲取最慢的10個用例的執行耗時
pytest --durations=10

2、新建run.py文件運行,代碼如下:

pytest.main(["-m","demo","--html=Report/report.html"])

九、pytest框架下的核心配置文件 pytest.ini

     ——配置在工程的根目錄下,可以全局生效

import pytest

@pytest.mark.webui
def test_01():
    print('web01')

@pytest.mark.webui
def test_02():
    print('web02')

@pytest.mark.interface
def test_03():
    print('interface01')

@pytest.mark.interface
def test_04():
    print('interface02')

if __name__ == "__main__":
    pytest.main(['-s','test_case2.py'])
  • pytest.ini

——ini 文件的名稱必須是pytest.ini,代碼中所標黃名稱必須這樣定義,不可為其他的,否則不起作用

[pytest]

#標簽
markers =
   webui: automation for web ui   #冒號后邊是標簽的注釋
   interface: automation for interface
   temp: just for fun

#定義到pytest中,讀取哪一些py文件,作為pytest的識別對象
python_files = cema*.py  test*.py

#配置后,會根據指定的 cema*,運行代碼時,只會執行cema打頭的py文件
#添加多個,逗號隔開,可繼續添加


#定義到pytest中,讀取哪一些類,作為pytest的識別對象
python_classes = Cema*

#配置后,類名為開頭Cema的類,就可以正常被識別到執行了


#指定運行的路徑,可以為絕對路徑和相對路徑
testpaths = cema_case2.py ./ 


#指定運行的函數名
python_functions = vip*  test* #單獨配置后,只會運行vip開頭的函數;空格間隔,可配置多個


#打印信息顯示Passed通過/Failed不通過
log_cli = True
#log_cli 和 -v的區別:
#log_cli會把測試用例的執行顯示的更加完善,會顯示哪個py文件下哪個類哪個函數,如:test_case.py::TestDemo::test_001 類:test_001
PASSED
# -v 會把【test session starts】打印信息中的信息更加的全面


#配置命令
addopts = -s -v #配置后,工程py文件下都可使用,pytest.main()中可不用在寫命令

 十、斷言機制

  • conftest.py
import pytest

@pytest.fixture(scope='session') #字符串中還可以是:module、class、funtion
def keep(): # 預先配置下
    return 1
  • pytest.ini
[pytest]

markers =
   webui: automation for web ui
   interface: automation for interface
   temp: just for fun

python_files = cema*.py

#python_classes = Test*

estpaths = cema_case2.py

python_functions = test*

log_cli = True

addopts = -s
  •  cema_case.py——【返回通過】的用例
import pytest

@pytest.mark.webui
def test_01(keep):
    assert keep == 1,"成功"

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

 結果:

  •  cema_case.py——【返回不通過】的用例
import pytest

@pytest.mark.webui
def test_01(keep):
    assert keep == 2,"失敗"

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

 結果:

 十一、生成測試報告

——pytest-html 測試報告模塊,cmd中安裝

pip install pytest-html

安裝后,我們配置完了代碼運行結束后,想要生成報告,在Terminal(終端)中輸入: 

pytest  #輸入pytest 后,點擊Enter(回車鍵)

pytest --html=./report/repott.html # =后邊是指定路徑、文件名稱

pytest --html=./report1/repott1.html --self-contained-html #直接生成html文件,不會帶有css樣式文件,發送給其他人時操作簡單
#可放在pytest.ini中的
addopts中

 在指定的目錄下會生成:repott.html

 測試報告:

十二、常用斷言

pytest 里面斷言實際上就是 python 里面的 assert 斷言方法,常用的有以下幾種:
  • assert xx :判斷 xx 為真
  • assert not xx :判斷 xx 不為真
  • assert a in b :判斷 b 包含 a
  • assert a == b :判斷 a 等於 b
  • assert a != b :判斷 a 不等於 b

十三、assert小栗子

想在拋出異常之后輸出一些提示信息,執行之后就方便查看是什么原因了

import pytest
# 異常信息
def f():
    return 3
def test_function():
    a = f()
    assert a % 2 == 0, "判斷 a 為偶數,當前 a 的值為:%s" % a

if __name__ == '__main__':
    pytest.main(['-s','demo.py'])

結果:

 十四、pytest的mark擴展使用

  • @pytest.mark.xfail(reason="")標記為失敗,期望值為失敗,在函數之上使用,pytest.xfail()預期結果失敗,下面的代碼不會執行,在函數里使用

  • @pytest.mark.skip()無條件跳過測試用例,pytest.mark.skipif()有條件跳過測試用力

  • @pytest.mark.parametrize(param1,[1,2,3])多組參數單個用例的執行

  • @pytest.mark.run需要插件pytest-ordering # 控制函數執行順序,@pytest.mark.run(order=1)

  • @pytest.mark.flaky

    最多失敗重跑5次,如果失敗延遲2秒重跑,可以結合mark標記使用,@pytest.mark.flaky(reruns=5,reruns-delay=2)

    如果是批量執行,命令為:pytest --reruns 5 --reruns-delay2

 十五、pytest常用插件擴展

  • pytest-cov代碼覆蓋率的檢測 pytest --cov=src --cov-report = html

  • pytest -sugar 改變pytest默認外觀,增加進度條功能

  • pytest-xdist 並行運行,pytst -n 2

  • pytest-rerunfailures 失敗用例重跑

  • pytest-ordering 執行順序

  • pytest-picked 僅測試上次提交以來已更改的代碼

十六、測試用例的執行

目錄:

1、主函數模式

(1)運行所有 pytest.main()

(2)指定模塊 pytest.main(’[-vs],’,’./testcase/test_day1.py’)

(3)指定目錄pytest.main(’[-vs]’),’./testcase’)

(4)通過nodeid指定用例運行:nodeid由模塊名,分隔符,類名,方法名,函數名組成

#pytest.main(["-vs"],’./interface_testcase/test_day3.py::test_demo11’)

#pytest.main(["-vs"],’./interface_testcase/test_day3.py::TestLogin::test_01_qianghong1’)

2、命令行模式

(1)運行所有:pytest(2)指定模塊 pytest -vs ./testcase/test_day1.py

(3)指定目錄 pytest -vs ./testcase

(4)通過nodeid指定用例運行:nodeid由模塊名,分隔符,類名,方法名,函數名組成

pytest -vs ./interface_testcase/test_day3.py::test_demo11

pytest -vs ./interface_testcase/test_day3.py::TestLogin::test_01_qianghong1

參數詳解: -s:表示輸出調試信息,包括print打印的信息
-v顯示更詳細的信息
-vs一起使用
-n支持多線程或者分布式運行測試用例
#如 #pytest.main([’-vs’,’./testcase/test_day1.py’,’-n=2’])
# pytest -vs ./testcase/test_day1.py -n 2
#reruns==number 表示失敗用例重跑
#pytest -vs ./testcase/test_day2.py --reruns 2
#pytest.main([’–vs’,’./testcase/test_day2.py’,‘reruns=2’]) #失敗得的用例重跑兩次
#-x表示只要一個用例報錯,那么測試停止運行
#–maxfail=2 出現兩個失敗用例停止
#-k 根據測試用例的部分字符串指定測試用例 pytest -vs test_day2 -k “yang”

pytest--命令行常用參數:http://t.zoukankan.com/zouzou-busy-p-11295444.html


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM