pytest學(xué)習(xí)筆記

使用環(huán)境

  • python3
  • pycharm
  • pytest

pytest編寫規(guī)范

  • 測(cè)試文件以test_開頭或以_test結(jié)尾
  • 測(cè)試類以Test開頭口叙,并且不能帶有init方法
  • 測(cè)試函數(shù)以test_開頭

實(shí)例

  1. 設(shè)置pycharm為pytest運(yùn)行


    pytest
  2. 創(chuàng)建demo.py文件
# 加法
def add(a, b)
  return a + b
  1. 創(chuàng)建test_demo.py 文件
from src.demo import add
 
# 測(cè)試加法
def test_add():
    assert add(1, 2) == 3
    assert add(1, 0) == 1
    assert add(1, -1) == 0
  1. 使用pycharm執(zhí)行test_demo文件
  2. 使用命令行執(zhí)行pytest test_demo

常用的pytest第三方插件

插件安裝方式pip install 插件名稱

  • pytest-sugar 顯示執(zhí)行的進(jìn)度條
    pytest-sugar
  • pytest-assume 一次性執(zhí)行完所有測(cè)試用例止吐,不會(huì)因?yàn)槟硞€(gè)測(cè)試用例失敗而終止后面的用例算柳。如惨好,上面的例子就會(huì)因?yàn)槟硞€(gè)用例失敗而終止
def test_add():
    assert add(1, 2) == 3
    assert add(1, 0) == 2
    assert add(1, -1) == 0

執(zhí)行結(jié)果

執(zhí)行結(jié)果
可以看出assert add(1, -1) == 0的測(cè)試用例并沒(méi)有被執(zhí)行又兵。
使用pytest-assume

def test_add(self):
  pytest.assume(add(1, 2) == 3)
  pytest.assume(add(1, 0) == 2)
  pytest.assume(add(1, -1) == 0)
  • pytest-ordering 設(shè)置測(cè)試用例執(zhí)行順序
import pytest
class TestAdd():

    @pytest.mark.run(order=2)
    def test_add1(self):
        print('test_add1')
        pytest.assume(add(1, 2) == 3)

    @pytest.mark.run(order=1)
    def test_add2(self):
        print('test_add2')
        pytest.assume(add(1, -1) == 0)

# order=1先執(zhí)行竖配,order=2后執(zhí)行
  • pytest-selenium
  • pytest-play
  • pytest-rerunfailures 重新運(yùn)行失敗的測(cè)試用例
# pytest-rerunfailures執(zhí)行方式
pytest --reruns 3 test_demo.py  # 3代表失敗后重新運(yùn)行的次數(shù)哨鸭,如果加上正常運(yùn)行的次數(shù)總共是4次
  • pytest-allure 測(cè)試報(bào)告
  • pytest-datadir
  • pytest-datafiles

pytest參數(shù)化

通過(guò)@pytest.mark.parametrize裝飾器傳參

import pytest
# 加法
def add(a, b):
    return a + b

# 參數(shù)
@pytest.mark.parametrize(
  'a, b, re', [
        (3, 4, 7),
        (1, -1, 0),
        (1, 1.2, 2.2),
])
# 測(cè)試加法
def test_add(a, b, re):
    pytest.assume(add(a, b) == re)

通過(guò)讀取文件的形式參數(shù)化
讀json文件

import json
import pytest

# 讀取json文件
def load_json():
    with open('test.json', 'r') as f:
        re = json.load(f)
    return re

# 加法運(yùn)算
def add(a, b):
    return a + b

# 把讀到文件的數(shù)據(jù)通過(guò)參數(shù)傳遞
@pytest.mark.parametrize('a, b, re', load_json())
# 測(cè)試加法運(yùn)算
def test_add(a, b, re):
    pytest.assume(add(a, b) == re)

讀yaml文件

import yaml
import pytest

# 讀取yaml文件
def load_json():
    with open('test.yaml', 'r') as f:
        re = yaml.load(f)
    return re

# 加法運(yùn)算
def add(a, b):
    return a + b

# 把讀到文件的數(shù)據(jù)通過(guò)參數(shù)傳遞
@pytest.mark.parametrize('a, b, re', load_json())
# 測(cè)試加法運(yùn)算
def test_add(a, b, re):
    pytest.assume(add(a, b) == re)

說(shuō)明: pytest.mark.parametrize的第一個(gè)參數(shù)代表測(cè)試方法接收的參數(shù)個(gè)數(shù),第二個(gè)參數(shù)為接收的數(shù)據(jù)內(nèi)容。如果是通過(guò)讀文件的形式傳參需要注意讀出來(lái)的文件內(nèi)容是否和要傳的參數(shù)類型一致肛根。


pytest執(zhí)行級(jí)別

  1. setup_module 模塊級(jí)別

如果在單個(gè)模塊中有多個(gè)測(cè)試函數(shù)和測(cè)試類辫塌,則可以選擇實(shí)現(xiàn)以下方法(只會(huì)執(zhí)行一次)

def setup_module():
""" 模塊執(zhí)行前準(zhǔn)備工作 """

class Test1():
  """ 測(cè)試類1 """
class Test2():
  """ 測(cè)試類2 """

def teardown_module():
    """ 模塊執(zhí)行后的結(jié)束工作 """
  1. setup_class 類級(jí)別

在調(diào)用類的所有測(cè)試方法之前和之后調(diào)用以下方法(只會(huì)執(zhí)行一次)

class TestDemo():
  @classmethod
  def setup_class(cls):
      """ 類方法執(zhí)行前準(zhǔn)備工作 """
  
  def test_add(self):
    "" 測(cè)試方法 """
  
  @classmethod
  def teardown_class(cls):
    """ 類方法執(zhí)行后結(jié)束工作 """
  1. setup_method 方法級(jí)別

圍繞每個(gè)方法執(zhí)行之前之后調(diào)用以下方法(每個(gè)方法執(zhí)行之前都會(huì)執(zhí)行一次)

class Test1():
  def setup_method(self):
    """ 每個(gè)方法執(zhí)行之前準(zhǔn)備工作  """

  def test_add1(self):
    """ 測(cè)試方法1 """  
  
    def test_add2(self):
    """ 測(cè)試方法1 """ 
 
  def teardown_method(self):
    """ 每個(gè)方法執(zhí)行之后結(jié)束工作 """
  1. setup_function 函數(shù)級(jí)別

在執(zhí)行每個(gè)函數(shù)執(zhí)行,調(diào)用以下方法(只會(huì)執(zhí)行一次)

def setup_function():
    """ 函數(shù)執(zhí)行前準(zhǔn)備工作 """

def test_add1():
  """ 測(cè)試函數(shù)1 """ 

def test_add2():
  """ 測(cè)試函數(shù)2 """ 

def teardown_function():
    """ 函數(shù)執(zhí)行后結(jié)束工 """

pytest fixtures

fixture函數(shù)的作用:

  • 完成setup和teardown操作派哲,處理數(shù)據(jù)庫(kù)臼氨、文件等資源的打開和關(guān)閉
  • 完成大部分測(cè)試用例需要完成的通用操作,例如login芭届、設(shè)置config參數(shù)储矩、環(huán)境變量等
  • 準(zhǔn)備測(cè)試數(shù)據(jù),將數(shù)據(jù)提前寫入到數(shù)據(jù)庫(kù)褂乍,或者通過(guò)params返回給test用例持隧,等
  • 有獨(dú)立的命名,可以按照測(cè)試的用途來(lái)激活逃片,比如用于functions/modules/class/session

fixture 參數(shù)
scope參數(shù)
scope=function:每個(gè)test都運(yùn)行屡拨,默認(rèn)是function的scope
scope=class:每個(gè)class的所有test只運(yùn)行一次
scope=module:每個(gè)module的所有test只運(yùn)行一次
scope=session:每個(gè)session只運(yùn)行一次

1. 定義一個(gè)函數(shù)級(jí)別的fixture

import pytest

# 定義fixture函數(shù)
@pytest.fixture()
def fixture_func():
    print("\n這是一個(gè)fixture函數(shù),用來(lái)完成一些提前準(zhǔn)備")

def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1==1
def test_2(fixture_func):
    print('測(cè)試用例2')
    assert 2==2

函數(shù)級(jí)別的fixture執(zhí)行結(jié)果


函數(shù)級(jí)別執(zhí)行結(jié)果

2. 定義一個(gè)模塊級(jí)別的fixture

@pytest.fixture(scope="module")
def fixture_func():
    print("\n這是一個(gè)fixture函數(shù),用來(lái)完成一些提前準(zhǔn)備")

def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1==1

def test_2(fixture_func):
    print('測(cè)試用例2')
    assert 2 == 2

模塊級(jí)別的fixture執(zhí)行結(jié)果


module級(jí)別執(zhí)行結(jié)果

3. 定義一個(gè)session級(jí)別fixture
(1) 先創(chuàng)建一個(gè)conftest.py文件,輸入以下代碼
說(shuō)明:當(dāng)有測(cè)試用例調(diào)用pytest.fixture函數(shù)時(shí)题诵,pytest會(huì)自動(dòng)去找conftest.py文件里找pytest.fixture函數(shù)洁仗,不需要import层皱。

import pytest

@pytest.fixture(scope="session")
def fixture_func():
   print("\n這是一個(gè)fixture函數(shù),用來(lái)完成一些提前準(zhǔn)備")

(2) 創(chuàng)建第一個(gè)測(cè)試用例文件 test_demo.py

def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1 == 1

def test_2(fixture_func):
    print('測(cè)試用例2')
    assert 2 == 2

(3) 創(chuàng)建第二個(gè)測(cè)試用例文件 test_demo2.py

def test_1(fixture_func):
    print('測(cè)試用例3')
    assert 3 == 3

def test_2(fixture_func):
    print('測(cè)試用例4')
    assert 4 == 4

(4) 使用命令行執(zhí)行測(cè)試用例

$ pytest -s test_*

(5) session級(jí)別fixture執(zhí)行結(jié)果


session級(jí)別執(zhí)行結(jié)果

4. 加上yield的fixture函數(shù)
以上幾個(gè)級(jí)別都是相當(dāng)于steup的方法性锭,加上yield可以構(gòu)造出相當(dāng)于teardown的方法。如下

import pytest

def start_prepare():
    print("\n這是一個(gè)用來(lái)執(zhí)行測(cè)試之前的準(zhǔn)備工作")

def end_operation():
    print("\n這是一個(gè)用來(lái)執(zhí)行測(cè)試結(jié)束時(shí)的結(jié)束工作")

# fixture函數(shù)
@pytest.fixture(scope="module")
def fixture_func():
    print("測(cè)試開始")
    # 當(dāng)程序碰到y(tǒng)ield會(huì)先執(zhí)行yield前面的代碼叫胖,等代碼都執(zhí)行完后回到y(tǒng)ield處繼續(xù)執(zhí)行后面的代碼
    yield start_prepare() 
    end_operation()
 

# 測(cè)試用例
def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1==1

def test_2(fixture_func):
    print('測(cè)試用例2')
    assert 2 == 2

yield 執(zhí)行結(jié)果


yield執(zhí)行結(jié)果

下面來(lái)看下函數(shù)級(jí)別的fixture函數(shù)加上yield后的結(jié)果

import pytest

def start_prepare():
    print("\n這是一個(gè)用來(lái)執(zhí)行測(cè)試之前的準(zhǔn)備工作")

def end_operation():
    print("\n這是一個(gè)用來(lái)執(zhí)行測(cè)試結(jié)束時(shí)的結(jié)束工作")

@pytest.fixture()
def fixture_func():
    print("測(cè)試開始")
    yield start_prepare()
    end_operation()


# 測(cè)試用例
def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1==1

def test_2(fixture_func):
    print('測(cè)試用例2')
    assert 2 == 2

yield 執(zhí)行結(jié)果


執(zhí)行結(jié)果

說(shuō)明:
當(dāng)測(cè)試用例調(diào)用了fixture_func函數(shù)會(huì)先執(zhí)行fixture_func函數(shù)的代碼
執(zhí)行順序是:

  1. print("測(cè)試開始")
  2. start_prepare()
  3. print('測(cè)試用例1')
  4. assert 1==1
  5. yield
  6. end_operation()

5. 使用addfinalizer()方法實(shí)現(xiàn)類似teardown的方法

import pytest

def start_prepare():
    print("\n這是一個(gè)用來(lái)執(zhí)行測(cè)試之前的準(zhǔn)備工作")

@pytest.fixture()
def fixture_func(request):  # 注意需要加個(gè)request參數(shù)
    print("\n測(cè)試開始")

    def end_operation():
        print("\n這是一個(gè)用來(lái)執(zhí)行測(cè)試結(jié)束時(shí)的結(jié)束工作")
    # 主要是這句代碼來(lái)完成結(jié)束工作
    request.addfinalizer(end_operation)
    return start_operation()

# 測(cè)試用例
def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1==1

def test_2(fixture_func):
    print('測(cè)試用例2')
    assert 2 == 2

addfinalizer()方法執(zhí)行結(jié)果


addfinalizer()執(zhí)行結(jié)果

6. fixture參數(shù)化

import pytest

@pytest.fixture(params=[
    "參數(shù)1",
    "參數(shù)2"
])
def fixture_func(request):
    str = request.param
    print(f"\n{str}")


# 測(cè)試用例
def test_1(fixture_func):
    print('測(cè)試用例1')
    assert 1==1

執(zhí)行結(jié)果草冈,可以看出一個(gè)用例執(zhí)行了兩次

參數(shù)化執(zhí)行結(jié)果

調(diào)用fixture的三種方式
方式1: 在測(cè)試用例中直接調(diào)用它,把fixture函數(shù)當(dāng)做參數(shù)傳入瓮增,例如上面的例子怎棱。

方式2:使用@pytest.mark.usefixtures("fixture函數(shù)名稱")裝飾器

import pytest


@pytest.fixture()
def fixture_func():
    print("\n測(cè)試前準(zhǔn)備")


@pytest.mark.usefixtures("fixture_func")
def test_1():
    print("函數(shù)測(cè)試用例1")
    assert 1 == 1


@pytest.mark.usefixtures("fixture_func")
def test_2():
    print('函數(shù)測(cè)試用例2')
    assert 1 == 1


class Test1:
    @pytest.mark.usefixtures("fixture_func")
    def test_3(self):
        print('類測(cè)試用例1')
        assert 1 == 1

    @pytest.mark.usefixtures("fixture_func")
    def test_4(self):
        print('類測(cè)試用例2')
        assert 1 == 1


@pytest.mark.usefixtures("fixture_func")
class Test2:
    def test_5(self):
        print('類測(cè)試用例3')
        assert 1 == 1

    def test_6(self):
        print('類測(cè)試用例4')
        assert 1 == 1

執(zhí)行結(jié)果


@pytest.mark.usefixtures()執(zhí)行結(jié)果

方式3: 用autos調(diào)用fixture,autouse 默認(rèn)設(shè)置為False绷跑。 當(dāng)默認(rèn)為False拳恋,就可以選擇用上面兩種方式來(lái)試用fixture。 當(dāng)設(shè)置為True時(shí)砸捏,在一個(gè)session內(nèi)的所有的test都會(huì)自動(dòng)調(diào)用這個(gè)fixture谬运,所以用該功能時(shí)要謹(jǐn)慎小心。
@pytest.fixture(scope="function", autouse=True)

import pytest


@pytest.fixture(scope="function", autouse=True)
def fixture_func():
    print("\n測(cè)試前準(zhǔn)備")


def test_1():
    print("函數(shù)測(cè)試用例1")
    assert 1 == 1


def test_2():
    print('函數(shù)測(cè)試用例2')
    assert 1 == 1


class Test1:

    def test_3(self):
        print('類測(cè)試用例1')
        assert 1 == 1

    def test_4(self):
        print('類測(cè)試用例2')
        assert 1 == 1


class Test2:
    def test_5(self):
        print('類測(cè)試用例3')
        assert 1 == 1

    def test_6(self):
        print('類測(cè)試用例4')
        assert 1 == 1

執(zhí)行結(jié)果


autouse=True執(zhí)行結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末垦藏,一起剝皮案震驚了整個(gè)濱河市梆暖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掂骏,老刑警劉巖轰驳,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異冒黑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門缴挖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人坟比,你說(shuō)我怎么就攤上這事间影∽⒏停” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)巩割。 經(jīng)常有香客問(wèn)我裙顽,道長(zhǎng),這世上最難降的妖魔是什么宣谈? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任愈犹,我火速辦了婚禮,結(jié)果婚禮上闻丑,老公的妹妹穿的比我還像新娘漩怎。我一直安慰自己,他們只是感情好嗦嗡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布勋锤。 她就那樣靜靜地躺著,像睡著了一般侥祭。 火紅的嫁衣襯著肌膚如雪叁执。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天矮冬,我揣著相機(jī)與錄音谈宛,去河邊找鬼。 笑死胎署,一個(gè)胖子當(dāng)著我的面吹牛吆录,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硝拧,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼径筏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了障陶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤聊训,失蹤者是張志新(化名)和其女友劉穎抱究,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體带斑,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鼓寺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勋磕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妈候。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挂滓,靈堂內(nèi)的尸體忽然破棺而出苦银,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布幔虏,位于F島的核電站纺念,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏想括。R本人自食惡果不足惜陷谱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瑟蜈。 院中可真熱鬧烟逊,春花似錦、人聲如沸铺根。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)夷都。三九已至眷唉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間囤官,已是汗流浹背冬阳。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留党饮,地道東北人肝陪。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像刑顺,于是被迫代替她去往敵國(guó)和親氯窍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354