全功能Python測試框架:pytest


python通用測試框架大多數人用的是unittest+HTMLTestRunner,這段時間看到了pytest文檔,發現這個框架和豐富的plugins很好用,所以來學習下pytest.

 

pytest介紹:

pytest是一個非常成熟的全功能的Python測試框架,主要有以下幾個特點:

  • 簡單靈活,容易上手
  • 支持參數化
  • 能夠支持簡單的單元測試和復雜的功能測試,還可以用來做selenium/appnium等自動化測試、接口自動化測試(pytest+requests)
  • pytest具有很多第三方插件,並且可以自定義擴展,比較好用的如pytest-selenium(集成selenium)、pytest-html(完美html測試報告生成)、pytest-rerunfailures(失敗case重復執行)、pytest-xdist(多CPU分發)等
  • 測試用例的skip和xfail處理
  • 可以很好的和jenkins集成
  • report框架----allure 也支持了pytest

####

安裝pytest:

pip install -U pytest

 

 

驗證安裝的版本:

pytest --version

 

####

 

幾個pytest documentation中的例子:

例子1:

1.創建test_sample.py文件,創建一個方法、一個用例

import pytest

# content of test_sample.py
def func(x):
    return x + 1
def test_answer():
    assert func(3) == 5

 

 

命令行切換到文件所在目錄,執行測試(也可以直接在IDE中運行):

命令:pytest

 

這個測試返回一個失敗報告,因為func(3)不返回5。

####

例子2:

當需要編寫多個測試樣例的時候,我們可以將其放到一個測試類當中,如:

創建test_sample2.py文件

class TestClass:  
    def test_one(self):  
        x = "this"  
        assert 'h' in x  
  
    def test_two(self):  
        x = "hello"  
        assert hasattr(x, 'check') 

 

運行以上例子:

pytest -q test_sample2.py

 

從測試結果中可以看到,該測試共執行了兩個測試樣例,一個失敗一個成功。

同樣,我們也看到失敗樣例的詳細信息,和執行過程中的中間結果。-q即-quiet,作用是減少冗長,具體就是不再展示pytest的版本信息。

 

####

如何編寫pytest測試樣例

通過上面2個實例,我們發現編寫pytest測試樣例非常簡單,只需要按照下面的規則:

  • 測試文件以test_開頭(以_test結尾也可以)
  • 測試類以Test開頭,並且不能帶有 init 方法
  • 測試函數以test_開頭
  • 斷言使用基本的assert即可

運行模式

Pytest的多種運行模式,讓測試和調試變得更加得心應手,下面介紹5種常用的模式。

在介紹之前需要提醒一句,運行pytest時會找當前目錄及其子目錄中的所有test_*.py 或 *_test.py格式的文件以及以test開頭的方法或者class,不然就會提示找不到可以運行的case了。

 

1.運行后生成測試報告(htmlReport)

安裝pytest-html:

pip install pytest-html

 

運行模式:

pytest --html=report.html

 

報告效果還可以

在以上報告中可以清晰的看到測試結果和錯誤原因,定位問題很容易。

 

2.運行指定的case

當我們寫了較多的cases時,如果每次都要全部運行一遍,無疑是很浪費時間的,通過指定case來運行就很方便了。

例子代碼:

新建test_sample3.py

class TestClassOne(object):
    def test_one(self):
        x = "this"
        assert 't'in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, 'check')


class TestClassTwo(object):
    def test_one(self):
        x = "iphone"
        assert 'p'in x

    def test_two(self):
        x = "apple"
        assert hasattr(x, 'check')

 

運行模式:

模式1:直接運行test_se.py文件中的所有cases:

pytest test_sample3.py

 

模式2:運行ttest_sample3.py文件中的TestClassOne這個class下的兩個cases:

pytest test_sample3.py::TestClassOne

模式3:運行test_sample3.py文件中的TestClassTwo這個class下的test_one:

pytest test_sample3.py::TestClassTwo::test_one

 

注意:定義class時,需要以T開頭,不然pytest是不會去運行該class的。

3.多進程運行cases

  當cases量很多時,運行時間也會變的很長,如果想縮短腳本運行的時長,就可以用多進程來運行。

安裝pytest-xdist:

pip install -U pytest-xdist

 

運行模式:

pytest test_se.py -n NUM

其中NUM填寫並發的進程數。

4.重試運行cases

  在做接口測試時,有事會遇到503或短時的網絡波動,導致case運行失敗,而這並非是我們期望的結果,此時可以就可以通過重試運行cases的方式來解決。

安裝pytest-rerunfailures:

pip install -U pytest-rerunfailures

運行模式:

pytest test_se.py --reruns NUM

NUM填寫重試的次數。

 

5.顯示print內容

  在運行測試腳本時,為了調試或打印一些內容,我們會在代碼中加一些print內容,但是在運行pytest時,這些內容不會顯示出來。如果帶上-s,就可以顯示了。

運行模式:

pytest test_se.py -s

另外,pytest的多種運行模式是可以疊加執行的,比如說,你想同時運行4個進程,又想打印出print的內容。可以用:

pytest test_se.py -s -n 4

 

其他運行方法

執行目錄下所有用例


pytest testcase/

 

####

pytest參數

1、-K EXPRESSION

執行某個關鍵字的用例

用例要匹配給出的表達式;使用python的語法,匹配的范圍是文件名、類名、函數名為變量,用and來區分

 

運行pytest時帶-k參數

pytest -k "test and TestClass and not test_a" test.py

可以看出,test_a這個用例被取消選擇了,沒有運行了

3、--maxfail=num

當錯誤個數到達給定數時,退出測試,這里就不列舉實例了,結果與-x類似

4、-m MARKEXPR

只能運行有相應標識的測試用例,使用這個參數,測試用例要使用@pytest.mark.marker修飾

# content of test.py
import pytest
class TestClass(object):
     def test_one(self):
         '''new_etests'''
         x = "this"
         assert 'h' in x
 
     @pytest.mark.slow
     def test_two(self):
         '''new_sssetests'''
         x = "hello"
         assert hasattr(x, 'check')
         
     def test_a(self):
         assert 1==2

teste_two使用了@pytest.mark.slow來修飾

在使用時,使用如下參數

pytest –m slow test.py

從上圖中可以看出,只運行了一個我們帶有標識的用例。

注意,-m后面不能帶''號(單引號),只能帶“”(雙引號),不然識別不到

如果要運行多個標識的話,用表達式,如下

pytest -m "slow or faster" 運行有slow標識或 faster標識用例

pytest -m "slow and faster" 運行有slow和faster標識的用例

pytest -m "slow and not faster" 運行有slow和沒有faster標識的用例

5、 -v, --verbose
詳細結果

6、-q, --quiet
極簡結果顯示,簡化控制台的輸出,可以看出輸出信息和之前不添加-q不信息不一樣, 下圖中有兩個..點代替了pass結果

7、-s
輸入我們用例中的調式信息,比如print的打印信息等,我們在用例中加上一句 print(driver.title),我們再運行一下我們的用例看看,調試信息輸出

8、-V
可以輸出用例更加詳細的執行信息,比如用例所在的文件及用例名稱等

9、--junit-xml=path
輸出xml文件格式,在與jenkins做集成時使用

10、 --result-log=path
將最后的結果保存到本地文件中

注意:標黃的是經常使用的

####

pytest fixture 這個很重要!

1、fixture scope的范圍參數
之前使用@pytest.fixture(scope='module')來定義框架,scope的參數有以下幾種

function 每一個用例都執行
class 每個類執行
module 每個模塊執行(函數形式的用例)
session 每個session只運行一次,在自動化測試時,登錄步驟可以使用該session

2、調用fixture的三種方法
2.1函數或類里面方法直接傳fixture的函數參數名稱

from __future__ import print_function
import pytest
 
@pytest.fixture(scope='module')
def resource_a_setup(request):
    print('\nresources_a_setup()')
    def resource_a_teardown():
        print('\nresources_a_teardown()')
    request.addfinalizer(resource_a_teardown)
 
def test_1(resource_a_setup):
    print('test_1()')
 
def test_2():
    print('\ntest_2()')
 
def test_3(resource_a_setup):
    print('\ntest_3()')

運行

pytest -s -v test_sample4.py

這是setup teardown的pytest的用法

2.2使用裝飾器@pytest.mark.usefixtures()修飾需要運行的用例

import pytest


# test_fixture1.py


@pytest.fixture()
def test1():
    print('\n開始執行function')


@pytest.mark.usefixtures('test1')
def test_a():
    print('---用例a執行---')


@pytest.mark.usefixtures('test1')
class TestCase:

    def test_b(self):
        print('---用例b執行---')

    def test_c(self):
        print('---用例c執行---')


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

 

運行

pytest -s -v test_sample4.py

這是在每一個用例的前面都加了一下處理

2.3疊加usefixtures
如果一個方法或者一個class用例想要同時調用多個fixture,可以使用@pytest.mark.usefixture()進行疊加。注意疊加順序,先執行的放底層,后執行的放上層

import pytest
# test_fixture1.py
 
 
@pytest.fixture()
def test1():
    print('\n開始執行function1')
 
 
@pytest.fixture()
def test2():
    print('\n開始執行function2')
 
 
@pytest.mark.usefixtures('test1')
@pytest.mark.usefixtures('test2')
def test_a():
    print('---用例a執行---')
 
 
@pytest.mark.usefixtures('test2')
@pytest.mark.usefixtures('test1')
class TestCase:
 
    def test_b(self):
        print('---用例b執行---')
 
    def test_c(self):
        print('---用例c執行---')
 
 
if __name__ == '__main__':
    pytest.main(['-s', 'test_fixture1.py'])

3.usefixtures與傳fixture區別
如果fixture有返回值,那么usefixture就無法獲取到返回值,這個是裝飾器usefixture與用例直接傳fixture參數的區別。

當fixture需要用到return出來的參數時,只能講參數名稱直接當參數傳入,不需要用到return出來的參數時,兩種方式都可以。

4.fixture自動使用autouse=True
當用例很多的時候,每次都傳這個參數,會很麻煩。fixture里面有個參數autouse,默認是False沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了

autouse設置為True,自動調用fixture功能

import pytest
# test_fixture1.py
 
 
@pytest.fixture(scope='module', autouse=True)
def test1():
    print('\n開始執行module')
 
 
@pytest.fixture(scope='class', autouse=True)
def test2():
    print('\n開始執行class')
 
 
@pytest.fixture(scope='function', autouse=True)
def test3():
    print('\n開始執行function')
 
 
def test_a():
    print('---用例a執行---')
 
 
def test_d():
    print('---用例d執行---')
 
 
class TestCase:
 
    def test_b(self):
        print('---用例b執行---')
 
    def test_c(self):
        print('---用例c執行---')
 
 
if __name__ == '__main__':
    pytest.main(['-s', 'test_fixture1.py'])

這樣就不用每一個用例去加裝飾器了,清爽了很多,

 

5.conftest.py的作用范圍

一個工程下可以建多個conftest.py的文件,一般在工程根目錄下設置的conftest文件起到全局作用。在不同子目錄下也可以放conftest.py的文件,作用范圍只能在改層級以及以下目錄生效。

'''scope=session'''

'''fixture為session級別是可以跨.py模塊調用的,也就是當我們有多個.py文件的用例時候,如果多個用例只需調用一次fixture,那就可以設置為scope="session",並且寫到conftest.py文件里'''

'''conftest.py文件名稱是固定的,pytest會自動識別該文件。放到工程的根目錄下,就可以全局調用了,如果放到某個package包下,那就只在該package內有效'''

'''如果想同時運行test_fixture11.py和test_fixture12.py,在cmd執行

> pytest -s test_fixture11.py test_fixture12.py
'''

 5.1conftest在不同的層級間的作用域不一樣

# conftest層級展示/conftest.py
 
import pytest
 
 
@pytest.fixture(scope='session', autouse=True)
def login():
    print('----准備登錄----')
 
 
# conftest層級展示/sougou_login/conftest
import pytest
 
 
@pytest.fixture(scope='session', autouse=True)
def bai_du():
    print('-----登錄百度頁面-----')
 
 
# conftest層級展示/sougou_login/login_website
import pytest
 
 
class TestCase:
    def test_login(self):
        print('hhh,成功登錄百度')
 
 
if __name__ == '__main__':
    pytest.main(['-s', 'login_website.py'])

###

 5.2conftest是不能跨模塊調用的(這里沒有使用模塊調用)

 

# conftest層級演示/log/contfest.py
import pytest
 
 
@pytest.fixture(scope='function', autouse=True)
def log_web():
    print('打印頁面日志成功')
 
 
 
 
# conftest層級演示/log/log_website.py
import pytest
 
 
def test_web():
    print('hhh,成功一次打印日志')
 
 
def test_web1():
    print('hhh,成功兩次打印日志')
 
 
if __name__ == '__main__':
    pytest.main(['-s', 'log_website.py'])

 

###

pytest之mark的使用,這個也很重要

使用方法:

1、注冊標簽名

2、在測試用例/測試類前面加上:@pytest.mark.標簽名

      打標記范圍:測試用例、測試類、模塊文件

 

注冊方式:

1、單個標簽:

在conftest.py添加如下代碼:
def pytest_configure(config):
    # demo是標簽名
    config.addinivalue_line("markers", "demo:示例運行") 

2、多個標簽:   

在conftest.py添加如下代碼:
def pytest_configure(config):
    marker_list = ["testdemo", "demo", "smoke"]  # 標簽名集合
    for markers in marker_list:
        config.addinivalue_line("markers", markers)

3、添加pytest.ini 配置文件(在你項目的任意一個文件下,新建一個file,文件命名為pytest.ini)

    

[pytest]
markers=
    smoke:this is a smoke tag
    demo:demo
    testdemo:testdemo

 

使用方法:

  

復制代碼
import pytest


@pytest.mark.testdemo
def test_demo01():
    print("函數級別的test_demo01")


@pytest.mark.smoke
def test_demo02():
    print("函數級別的test_demo02")


@pytest.mark.demo
class TestDemo:
    def test_demo01(self):
        print("test_demo01")

    def test_demo02(self):
        print("test_demo02")
復制代碼

 

運行方式:

  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"])

 

 

 

###

####


免責聲明!

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



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