使用環(huán)境
- python3
- pycharm
- pytest
pytest編寫規(guī)范
- 測(cè)試文件以test_開頭或以_test結(jié)尾
- 測(cè)試類以Test開頭口叙,并且不能帶有init方法
- 測(cè)試函數(shù)以test_開頭
實(shí)例
-
設(shè)置pycharm為pytest運(yùn)行
- 創(chuàng)建
demo.py
文件
# 加法
def add(a, b)
return a + b
- 創(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
- 使用pycharm執(zhí)行test_demo文件
- 使用命令行執(zhí)行
pytest test_demo
常用的pytest第三方插件
插件安裝方式pip install 插件名稱
-
pytest-sugar
顯示執(zhí)行的進(jìn)度條
-
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é)果
使用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í)別
-
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é)束工作 """
-
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é)束工作 """
-
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é)束工作 """
-
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é)果
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é)果
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é)果
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é)果
下面來(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é)果
說(shuō)明:
當(dāng)測(cè)試用例調(diào)用了fixture_func函數(shù)會(huì)先執(zhí)行fixture_func函數(shù)的代碼
執(zhí)行順序是:
- print("測(cè)試開始")
- start_prepare()
- print('測(cè)試用例1')
- assert 1==1
- yield
- 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é)果
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í)行了兩次
調(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é)果
方式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é)果