pytest测试框架

识别的测试文件

test_*.py

*_test.py

识别的用例

Test*类包含的所有test_*的方法(测试类不能带有__init__方法)

不在class中的所有的test_*方法

pytest也可以执行unittest框架写的用例和方法

控制台执行

直接执行 pytest 文件名

pytest -v 文件名 输出详细信息

pytest -s 文件名 把测试用例中print的内容也打印出来

pytest 文件名.py::类名 运行某个模块中的某个类

pytest 文件名.py::类名::方法名 运行某个模块中的某个类里面的方法

pytest -v -k “类名 and not 方法名” 跳过执行某个用例

pytest -m [标记名] @pytest.mark.[标记名] 将运行有这个标记的测试用例

pytest -x 文件名 一旦运行到报错就停止运行

pytest --maxfail=[num] 当运行错误达到num时就停止运行

测试失败后要重新运行n次,要在重新运行之间添加延迟时间,间隔n秒再运行

安装 pip install pytest-rerunfailures

执行 pytest --reruns 3 -v -s 文件名.py 重新运行3次

执行 pytest -v --reruns 5 --reruns-delay 1 重新运行5次,每次运行间隔1秒

同一方法内写多条断言,第一条报错后,后面也继续执行

安装 pip install pytest-assume

pytest.assume(1==4)

pycharm中运行

主函数中 pytest.main("-v -x 模块名") 在引号中添加所需要的参数

主函数中 pytest.main([’-v’,’-x’])

pytest框架结构

模块级(setup_module/teardown_module)模块始末,全局的(优先级最高)

函数级(setup_function/teardown_function)只对函数用例生效(不在类中)

类级(setup_class/teardown_class)只在类中前后执行一次(在类中)

方法级(setup_method/teardown_method)开始于方法始末(在类中)

类里面的(setup/teardown)运行在调用方法的前后

pytest-fixture的用法

场景

用例1需要登录

用例2不需要登录

用例3需要登录

步骤

引入pytest

在登录的函数上面加上@pytest.fixture()

在要使用的测试方法中传入(登录函数名称),就先登录

不传入的就不登录直接执行测试方法

import pytest


@pytest.fixture()
def login():
    print('login')


def test_one(login):
    print('one')
    assert 1 == 1


def test_two():
    print('two')
    assert 2 == 2


def test_three(login):
    print('three')
    assert 3 == 3

# 入口函数
if __name__ == '__main__':
    ## ??好像没有生效,后面再看看
    ## 解释器(python解释器才支持)的问题
    ## 要写成list,字符串格式高版本不支持
    pytest.main("-v --reruns 3")

conftest.py

用来放置公共的方法,测试的文件中可以直接使用里面的方法,不需要引入

场景:与其他人员合作开发时,公共模块要在不同文件中,要在大家都能访问的地方

解决:conftest.py这个文件进行数据共享,并且他可以放在不同位置起着不同的范围共享作用

执行:系统执行到参数login时先从本文件中查找是否有这个文字的变量,之后在conftest中找是否有

步骤:将登录模块带@pytest.fixture写在conftest.py中

注意点

conftest文件名不能更改

conftest.py与运行的用例要在同一个package下,并且有__init__.py文件

不需要import导入conftest.py,pytest会自动查找

全局的配置和前期工作都可以写在这里,放在某个包下,就是这个包数据共享的地方

yield

import pytest

# yield 在第一次执行到yield时,只执行yield之前的内容
# 第二次执行到yield时,只执行yield之后到内容
@pytest.fixture(scope="module")
def login():
    print('login')

    yield

    print('clear')


def test_one(login):
    print('one')
    assert 1 == 1


def test_two():
    print('two')
    assert 2 == 2


def test_three(login):
    print('three')
    assert 3 == 3


if __name__ == '__main__':
    ## ??好像没有生效,后面再看看
    pytest.main("-v --reruns 3")

结果

test_python_file.py login
.one
.two
.three
clear

@pytest.fixture(autouse=True) 将该方法自动应用到每条测试用例中

import pytest


@pytest.fixture(autouse=True)
def login():
    print('login')


def test_one():
    print('one')
    assert 1 == 1


def test_two():
    print('two')
    assert 2 == 2


def test_three():
    print('three')
    assert 3 == 3


if __name__ == '__main__':
    ## ??好像没有生效,后面再看看
    pytest.main("-v --reruns 3")

结果

test_python_file.py login
.one
login
.two
login
.three

参数化

@pytest.mark.parametrize()

@pytest.mark.parametrize("content,expected", [("3+5", 8), ("2+5", 7), ("7*5", 35)])
def test_eval(content, expected):
    assert eval(content) == expected


@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [3, 4])
def test_foo(x, y):
    print(f"x的值为{x}")
    print(f"y的值为{y}")
    # 共输出4组数据,(1,3)(2,3)(2,3)(2,4)


test_user_data = ['xiaoming', 'xiaohong']


@pytest.fixture(scope="module")
def login_r(request):
    # 接收传入的参数
    user = request.param
    print(f"login_r登录的用户为{user}")
    return user


# indirect=True 可以把传过来的参数当函数执行
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
    a = login_r
    print(f"login登录的用户为{a}")

对应结果

test_python_file.py ....x的值为1
y的值为3
.x的值为2
y的值为3
.x的值为1
y的值为4
.x的值为2
y的值为4
login_r登录的用户为xiaoming
.login登录的用户为xiaoming
login_r登录的用户为xiaohong
.login登录的用户为xiaohong

跳过测试用例

@pytest.mark.skip()

@pytest.mark.skipif()

# 跳过测试用例
@pytest.mark.skip("此次测试不执行该条用例")
def test_skip():
    print("这条测试用例被跳过了")


# 在满足条件时,跳过测试用例
@pytest.mark.skipif(sys.platform == 'darwin', reason="不在macOs上执行")
def test_skip_if():
    print('该条测试用例有条件的被跳过')

对应结果:

test_python_file.py s
Skipped: 此次测试不执行该条用例
s
Skipped: 不在macOs上执行

功能测试尚未实施或尚未修复的错误,当用例执行成功时,执行结果为xpass;当用例执行失败时,执行结果为xfail

@pytest.mark.xfail

@pytest.mark.xfail
def test_fail():
    print('test_xfail')
    raise Exception


@pytest.mark.xfail
def test_pass():
    print('test_xfail')

对应结果

test_python_file.py xtest_xfail

@pytest.mark.xfail
    def test_fail():
        print('test_xfail')
>       raise Exception
E       Exception

test_python_file.py:52: Exception
Xtest_xfail
                                                   [100%]

======================== 1 xfailed, 1 xpassed in 0.09s =========================
Process finished with exit code 0

自定义标记mark只执行某部分用例

@pytest.mark.XXX 其中XXX为自定义标签

import pytest
import sys


@pytest.mark.login
def test_login_1():
    print("test_login_1")


@pytest.mark.login
def test_login_2():
    print("test_login_2")


@pytest.mark.login
def test_login_3():
    print("test_login_3")


@pytest.mark.search
def test_search_1():
    print("test_search_1")


@pytest.mark.search
def test_search_2():
    print("test_search_2")


@pytest.mark.search
def test_search_3():
    print("test_search_3")

conftest.py中的文件

def pytest_configure(config):
    markers_list = ["search", "login"]
    for markers in markers_list:
        config.addinivalue_line(
            "markers", markers
        )

控制台执行

pytest test_python_file.py -m login

执行结果

=================================================== test session starts ====================================================
platform darwin -- Python 3.7.3, pytest-5.4.3, py-1.8.1, pluggy-0.13.1
rootdir: /Users/yuanmeng/Desktop/lesson_script/fund/gdp
plugins: rerunfailures-9.0, metadata-1.9.0, allure-pytest-2.8.13, assume-2.2.1, html-2.1.1, forked-1.1.3, xdist-1.32.0
collected 6 items / 3 deselected / 3 selected                                                                              

test_python_file.py ...                                                                                              [100%]

============================================= 3 passed, 3 deselected in 0.05s ==============================================

多线程并行执行用例

pytest分布式执行插件:pytest-xdist

多个cpu或主机执行前提:用例之间是独立的,没有先后顺序,随机都能执行,可重复运行不影响其他用例

安装:pip3 install pytest-xdist

多个cpu并行执行用例,直接加 -n 3,其中3是并行数量

pytest-html生成报告

安装:pip install pytest-html

生成html报告:pytest -v -s --html=report.html --self-contained-html

pytest 高级用法

pytest --collect-only 只收集测试用例不运行

-k 满足表达式的都会执行

-m 满足标签的才会执行(测试用例添加 pytest.mark.标签名)

pytest --junit-xml=path 生成xml格式的测试结果

测试用例执行顺序

unittest 测试用例名称的编码为顺序

pytest以编写测试用例的先后顺序

pytest-ordering 修改测试用例的执行顺序

import pytest

@pytest.mark.run(order=2)
def test_foo():
    assert True

@pytest.mark.run(order=1)
def test_bar():
    assert True

执行结果

$ py.test test_foo.py -vv
============================= test session starts ==============================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
plugins: ordering
collected 2 items

test_foo.py:7: test_bar PASSED
test_foo.py:3: test_foo PASSED

=========================== 2 passed in 0.01 seconds ===========================

pytest测试用例执行顺序

import pytest

# fixtures documentation order example
order = []


@pytest.fixture(scope="session")
def s1():
    order.append("s1")


@pytest.fixture(scope="module")
def m1():
    order.append("m1")


@pytest.fixture
def f1(f3):
    order.append("f1")


@pytest.fixture
def f3():
    order.append("f3")


@pytest.fixture(autouse=True)
def a1():
    order.append("a1")


@pytest.fixture
def f2():
    order.append("f2")


def test_order(f1, m1, f2, s1):
    assert order == ["s1", "m1", "a1", "f3", "f1", "f2"]

执行结果

s1: is the highest-scoped fixture (session).

m1: is the second highest-scoped fixture (module).

a1: is a function-scoped autouse fixture: it will be instantiated before other fixtures within the same scope.

f3: is a function-scoped fixture, required by f1: it needs to be instantiated at this point

f1: is the first function-scoped fixture in test_order parameter list.

f2: is the last function-scoped fixture in test_order parameter list.
执行命令时加上参数 --setup-show 可以查看执行顺序

pytest.ini 改变pytest的运行行为

[pytest]

addopts = -v -s

python_files = 'abc*'

python_classes = 'Abc*'

python_functions = 'abc*'

pytest工厂模式

@pytest.fixture
def make_customer_record():
    def _make_customer_record(name):
        return {"name": name, "orders": []}

    return _make_customer_record


def test_customer_records(make_customer_record):
    customer_1 = make_customer_record("Lisa")
    customer_2 = make_customer_record("Mike")
    customer_3 = make_customer_record("Meredith")
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值