pytest学习

官方文档:Full pytest documentation — pytest documentationhttps://docs.pytest.org/en/latest/contents.html

plugin解读:https://github.com/potatoImp/pytestCodeParsinghttps://github.com/potatoImp/pytestCodeParsing

 优秀文章参考:​​​​​​​基于 Pytest 框架的自动化测试开发实践 (万字长文入门篇) · TesterHomehttps://testerhome.com/topics/23441

基本逻辑:前置脚本失败,用例和后置脚本不执行,用例结果error;用例失败,前后置脚本执行,用例结果failed;后置脚本失败,用例结果1passed,1error

一、运行方式

命令行方式

        1.pytest -sv:-s打印用例中print内容,-v打印用例文件路径、类、方法信息

        2.pytest -n 次数:多线程或者分布式???

        3.pytest --reruns 次数:失败用例重复跑几次

        4.python -m pytest:模块化执行

          python -m把当前工作目录当作模块执行,解决import工作目录模块报错

        5.

          pytest 目录:执行目录下符合条件的用例

          pytest 目录\文件.py::类(Test*)::方法(test_*):执行一个类或者一个方法

          pytest -k "名字":根据名字执行

          pytest -m:标签执行

          pytest 目录\文件.py:指定模块执行

6.pytest -m:标签执行

@pytest.mark.名称    #下面接类或者方法,给类、方法增加标签
class类或者def用例

pytestmark = pytest.mark.名称    #模块增加标签(模块指整个文件)

pytest -m 名称    #执行调用标签

这样使用marker未注册在执行后会警告,PytestUnknownMarkWarning,在ini文件中注册

pytest.ini配置文件运行

[pytest]
#命令行参数,用空格分割
addopts = -vs -reruns 2 -m "smoke or wait"
#测试用例文件夹,可自己配置
testpaths = ./testcase
#配置测试搜索的模块文件名称
python_files = test*.py
#配置测试搜索的测试类名
python_classes = Test*
#配置测试搜索的测试函数名
python_functions = test
#mark运行,不加有warning
markers = 
    smoke:function test
    wait:wait test

"""
    开头必须是[pytest]
    文件放在项目根目录下
    notepad++打开后选择ansi编码
"""

二、fixture

@pytest.fixture

  • fixture是一个函数,在函数上添加注解@pytest.fixture来定义

  • 定义在conftest.py中,无需import就可以调用

  • 定义在其他文件中,import后也可以调用

  • 定义在相同文件中,直接调用

执行顺序

# 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):    
"""
    f1依赖f3
    fixture装饰的函数入参,只能是其他fixture。
"""
    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"]

"""
    session>module>class>function
    默认fixture是scope = function
    如果scope相同,就按test调用先后顺序,以及fixture之间的依赖关系。

    autouse的fixture会优先于相同scope的其他fixture。
"""

fixture嵌套

@pytest.fixture()
def a():
    print("1步骤")


@pytest.fixture()
def b(a):
    print("2步骤")


def test_order(b):
    pass


"""
    输出:1步骤\n2步骤
    fixture装饰的函数入参,只能是其他fixture。
"""

request

相当于pytest的内置fixture

request.param获取测试的数据,实现fixture参数化

data = [{"user": "admin1", "pwd": "111"}, {"user": "admin2", "pwd": "222"}, {"user": "admin3", "pwd": "333"}]


@pytest.fixture(params=data)
def test(request):    #request不能自动联想
    data = request.param["user"]
    return data


def test_compare(test):
    data = test    #这里test理解为request类的实例化
    print(data)

"""
    输出3次user1、user2、user3
"""

request.config获取配置文件参数

#conftest.py

def pytest_addoption(parser):
    parser.addoption(
        "--whysohard", action="store", default="777", help="pytest为什么这么难啊"
    )

@pytest.fixture()
def test(request):    #相当于fixture嵌套
    return request.config.getoption("--whysohard")
    #reture request.comfig.getoption("whysohard")用法一样,命令行参数前不用--
class Test_test:
    def testcase1(self,request):
        data = request.config.getoption("--whysohard")
        print(data)

    def testcase2(self,test):
        data = test
        print(data)

"""
    这里用request或者fixture名称都可以
"""

request.module反向获取用例中

@pytest.fixture(scope="module")
def smtp(request):
    server = getattr(request.module, "smtpserver", "开始了没有啊")
    print(server)
    yield
    print("结束了")


smtpserver = "开始了"


def test_showhelo(smtp):
    print("正在执行")

request成员对象

@pytest.fixture(autouse=True)
def print_request(request):
    print("\n=======================request start=================================")
    print(request.module)
    print(request.function)
    print(request.cls)
    print(request.fspath)
    print(request.fixturenames)
    print(request.fixturename)
    print(request.scope)
    print("\n=======================request end=================================")
def test_answer_1(request):
    pass

"""
    输出:
         =======================request start=================================
         <module 'web.cases.module2.test_1' from 'D:\\web\\cases\\module2\\test_1.py'>
         <function test_answer_1 at 0x0000012D1C9FD9D8>
         None
         D:\web\cases\module2\test_1.py
         ['_verify_url', 'base_url', '__pytest_repeat_step_number', 'show_request', 'request']
         show_request
         function
         =======================request end=================================
"""

三、marker

usefixtures 、filterwarnings 、skip 、skipif 、xfail五种用法

自定义marker见一.6

四、参数化

1.@pytest.mark.parametrize()

多个变量用tuple,多个tuple用list        

@pytest.mark.parametrize(("num1", "num2", "exp"), [(1, 2, 3), (2, 3, 5), (2, 1, 3)])
def testcase(num1, num2, exp):
    result = num1 + num2
    assert result == exp

2.@pytest.fixture()

见request.param用法

3.参数化后对某一用例添加marker

参数化后会生成多个tests,如果有些test需要marker,可以用pytest.param来添加

marker方式

@pytest.mark.parametrize(
    "test_input,expected",
    [("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
)
def test_eval(test_input, expected):
    assert eval(test_input) == expected

fixture方式

@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
def data_set(request):
    return request.param
def test_data(data_set):
    data = data_set
    print(data)

4.pytest_generate_tests

用来自定义参数化方案

def pytest_generate_tests(metafunc):
"""
    metafunc有5个属性,fixturenames,module,config,function,cls
    metafunc.fixturenames:参数化收集时的参数名称
    metafunc.module:使用参数名称进行参数化的测试用例所在的模块d对象
    metafunc.config:测试用例会话
    metafunc.function:测试用例对象,即函数或方法对象
    metafunc.cls: 测试用例所属的类的类对象
    
"""
    test_method_name = metafunc.function.__name__
    test_class_name = metafunc.cls.__name__
    param = get_arg(test_class_name, test_method_name)
    // *param 传入两个参数:1.元组 2.列表嵌套元组
    metafunc.parametrize(*param)

def get_arg(test_class_name, test_method_name):
    # test_class_name = self.__class__.__name__
    if test_class_name.startswith("TestApp"):
        data = yaml_metis_app_android_data[test_class_name][test_method_name]
    else:
        data = yaml_metis_web_case[test_class_name][test_method_name]
    arg_values = arg_names = []
    if isinstance(data, dict):
        arg_names = tuple(data.keys())    // tuple() 转换成元组数据
        # arg_names = ",".join(data.keys())
        arg_values.append(tuple((data.values())))
    elif isinstance(data, list):
        item = data[0]
        arg_names = tuple(item.keys())
        for d in data:
            arg_values.append(tuple((d.values())))
    return arg_names, arg_values

其他

1.pycharm使用pytest执行

file-setting-Python Intergrated Tools(或者直接搜pytest)选择pytest执行,否则默认为unittest方式执行

 删除其他configuration,默认使用pytest执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值