zoukankan      html  css  js  c++  java
  • python 自动化测试 pytest 的使用

    pytest 是一款以python为开发语言的第三方测试,主要特点如下:

    1.  比自带的 unittest 更简洁高效,兼容 unittest框架

    2. 支持参数化

    3. 可以更精确的控制要测试的测试用例

    4. 丰富的插件,已有300多个各种各样的插件,也可自定义扩展,如pytest-selenium、pytest-html、pytest-rerunfailures、pytes-xdish

    5. 可很好的和CI工具结合

     

    安装

    pip install pytest
    
    

     

    测试用例编写规则

    1. 测试文件以test_开头 或者 _test结尾

    2. 测试类以Test开头,并且不能带有 init 方法

    3. 测试文件以 test_开头

    4. 断言使用基本的 assert 即可

    pytest会递归查找当前目录及子目录下所有 以test_开始 或者 _test结尾的python脚本,执行其中符合规则的函数和方法,不需要显示调用

     

    运行命令:(cmd进入用例所在目录)

    1. pytest folder_name ======》直接运行文件夹内符合规则的所有用例
    2. pytest test_file.py ======》执行某个py文件中的用例
    3. pytest test_file.py::test_func ======》执行模块内的某个函数(节点运行)
    4. pytest test_file.py::TestClass::test_method ======》执行模块内测试类的某个方法(节点运行)
    5. pytest test_file.py::TestClass ======》执行模块内某个测试类(节点运行)
    6. pytest test_file.py::TestClass test_file2.py::test_mothod ======》多节点运行,中间用空格隔开
    7. pytest -k pass ======》匹配用例名称的表达式,含有“pass”的被执行,其他的deselected
    8. pytest -k "pass or fail" ======》组合匹配,含有“pass” 和 “fail”的被执行
    9. pytest -k "not pass" ======》排除运行,不含“pass”的被执行
    10. pytest -m finished ======》标记表达式,运行用@pytest.mark.finished 标记的用例
    11. pytest -m "finished and not merged" ======》多个标记逻辑匹配,运行含有finished 不含 merged标记的用例
    12. pytest -v ======》运行时显示详细信息
    13. pytest -s ======》显示打印消息
    14. pytest -x  ======》遇到错误就停止运行
    15. pytest -x --maxfail=2 ======》遇到两个错误就停止运行
    16. pytest --setup-show ======》跟踪固件运行
    17. pytest -v --reruns 5 --reruns-delay 1 ======》运行失败的用例间隔1s重新运行5次  pip install pytest-rerunfailures
    18. pytest ======》多条断言,报错后,后面的依然执行, pip install pytest-assume,断言 pytest.assume(2==4)
    19. pytest -n 3 ======》3个cpu并行执行测试用例,需保证测试用例可随机执行, pip install pytest-xdist分布式执行插件,多个cpu或主机执行
    20. pytest -v -n auto ======》自动侦测系统里cpu的数目
    21. pytest --count=2 ======》重复运行测试 pip install pytest-repeat
    22. pytest --html=./report/report.html ======》生成报告,此报告中css是独立的,分享时会丢失样式,pip install pytest-html
    23. pytest --html=report.html --self-containd-html ======》合并css到html报告中,除了passed所有行都被展开
    24. pytest --durations=10 ======》获取最慢的10个用例的执行耗时

     

    用例执行顺序控制

    pytest 用例执行顺序默认是按字母顺序去执行,要控制执行顺序,需要安装插件 pytest-ordering:pip install pytest-ordering

    在测试方法上加上装饰器:

    @pytest.mark.last 最后一个执行

    @pytest.mark.run(order=n) n=1则是第一个执行

     

    Mark

    标签的使用方法:

    注册标签名 / 内置标签---> 在测试用例  / 测试类 / 模块文件 前面加上 @pytest.mark.标签名

    注册方法:

    1. 在conftest.py 文件中添加代码

    # 单个标签文件内容
    def pytest_configure(config):
        config.addinivalue_line("markers", "demo:demo标签名称")
    
    # 多个标签文件内容
    def pytest_configure(config):
        marker_list = ["p0:p0级别用例", "p1:p1级别用例", "p2:p2级别用例"] # 标签名称
        for markers in marker_list:
            config.addinivalue_line("markers", markers)

    2. 项目中添加pytest.ini配置文件

    [pytest]
    markers = 
        p0:p0级别用例
        p1:p1级别用例
        p2:p2级别用例

    使用方法

    import pytest
    
    
    @pytest.mark.p0
    def test_mark01():
        print("函数级别的mark_p0")
    
    @pytest.mark.p1
    def test_mark02():
        print("函数级别的mark_p1")
    
    @pytest.mark.P2
    class TestDemo:
        def test_mark03(self):
            print("mark_p2")
    
        def test_mark04(self):
            print("mark_p2")

    运行方式:

    1. 命令行运行

    pytest -m "p0 and p1"

    2. 文件运行

    pytest.main(["-m", "P0", "--html=report.html"])

    内置标签:

    参数化:@pytest.mark.parametrize(argnames, argvalues)

    无条件跳过用例:@pytest.mark.skip(reason="xxx")

    有条件跳过用例:@pytest.mark.skipif(version < 0.3, reason = "not supported until 0.3")

    预测执行失败进行提示标记:@pytest.mark.xfail(version < 0.3, reason = "not supported until 0.3"),运行结果为X(通过xpassed,失败xfailed)

    # 参数化
    import hashlib
    @pytest.mark.parametrize(
    "x", list(range(10))) def test_somethins(x): time.sleep(1) @pytest.mark.parametrize("passwd",["123456", "abcdefgfs", "as52345fasdf4"]) def test_passwd_length(passwd): assert len(passwd) >= 8 @pytest.mark.parametrize('user, passwd',[('jack', 'abcdefgh'),('tom', 'a123456a')]) def test_passwd_md5(user, passwd): db = { 'jack': 'e8dc4081b13434b45189a720b77b6818', 'tom': '1702a132e769a623c1adb78353fc9503' } assert hashlib.md5(passwd.encode()).hexdigest() == db[user] # 如果觉得每组测试的默认参数显示不清晰,可以使用 pytest.param 的 id 参数进行自定义 @pytest.mark.parametrize("user, passwd", [pytest.param("jack", "abcdefgh", id = "User<Jack>"), pytest.param("tom", "a123456a", id = "User<Tom>")]) def test_passwd_md5_id(user, passwd): db = { 'jack': 'e8dc4081b13434b45189a720b77b6818', 'tom': '1702a132e769a623c1adb78353fc9503' } assert hashlib.md5(passwd.encode()).hexdigest() == db[user]

     

    Fixture

    固件:是一些函数,pytest会在执行函数之前或者之后加载运行它们,相当于预处理和后处理。

    fixture的目的是提供一个固定基线,在该基线上测试可以可靠地、重复的执行。

    1. 名称:默认为定义时的函数名,可以通过 @pytest.fixture(name="demo") 给fixture重命名
    2. 定义:在固件函数定义前加上@pytest.fixture();fixture是有返回值的,没return则返回None
    3. 使用:作为参数、使用usefixtures、自动执行(定义时指定autouse参数)
      • def test_demo(fixture_func_name)
      • @pytest.mark.usefixtures("fixture_func_name1", "fixture_func_name2") 标记函数或者类
    4. 预处理和后处理:用yield关键词,yield之前的代码是预处理,之后的是后处理
    5. 作用域:通过scope参数控制作用域
      • function:函数级,每个测试函数都会执行一次(默认)
      • class:类级别,每个测试类执行一次,所有方法都共享这个fixture
      • module:模块级别,每个模块.py执行一次,模块中所有测试函数、类方法 或者 其他fixture 都共享这个fixture
      • session:会话级别,每次会话只执行一次,一次会话中所有的函数、方法都共享这个fixture
    6. 集中管理:使用文件conftest.py 集中管理,在不同层级定义,作用于在其所在的目录和子目录,pytest会自动调用

    scope、yield、auto的使用

    # scope、yield、auto使用
    @pytest.fixture(scope = "function", autouse=True)
    def function_scope():
        pass
    
    @pytest.fixture(scope = "module", autouse=True)
    def module_scope():
        pass
    
    @pytest.fixture(scope = "session")
    def session_scope():
        pass
    
    @pytest.fixture(scope = "class", autouse=True)
    def class_scope():
        pass
    
    import time
    
    DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
    
    @pytest.fixture(scope='session', autouse=True)
    def timer_session_scope():
        start = time.time()
        print('
    session start: {}'.format(time.strftime(DATE_FORMAT, time.localtime(start))))
    
        yield
    
        finished = time.time()
        print('
    session finished: {}'.format(time.strftime(DATE_FORMAT, time.localtime(finished))))
        print('session Total time cost: {:.3f}s'.format(finished - start))
    
    def test_1():
        time.sleep(1)
    
    def test_2():
        time.sleep(2)
    
    '''
    执行命令:pytest --setup-show -s
    固件执行结果: test_pytest_study.py session start: 2020-04-16 17:29:02 SETUP S timer_session_scope SETUP M module_scope SETUP C class_scope SETUP F function_scope test_pytest_study.py::test_3 (fixtures used: class_scope, function_scope, module_scope, timer_session_scope). TEARDOWN F function_scope TEARDOWN C class_scope SETUP C class_scope SETUP F function_scope test_pytest_study.py::test_4 (fixtures used: class_scope, function_scope, module_scope, timer_session_scope). TEARDOWN F function_scope TEARDOWN C class_scope TEARDOWN M module_scope session finished: 2020-04-16 17:29:05 session Total time cost: 3.087s TEARDOWN S timer_session_scope '''

    使用文件conftest.py 集中管理

    # conftest.py
    # conding=utf-8
    import pytest
    
    @pytest.fixture()
    def postcode():
        print("执行postcode fixture")
        return "010"
    # test_demo.py
    # coding=utf-8
    import pytest
    
    class TestDemo():
    
        def test_postcode(self, postcode):
            assert postcode == "010"
    
    if __name__=="__main__":
        pytest.main(["--setup-show", "-s", "test_demo.py"])
    python test_demo.py
    执行过程:
    test_demo.py 执行postcode fixture
    
            SETUP    F postcode
            test_demo.py::TestDemo::test_postcode (fixtures used: postcode).
            TEARDOWN F postcode
    # 如果整个文件都用一个fixture,可以用pytestmark标记
    pytestmark = pytest.mark.usefixtures("login")

    fixture参数化

    固件参数化需要使用pytest内置的固件request,并通过 request.param 获取参数。

    # test_demo.py
    @pytest.fixture(params=[
        ("user1", "passwd1"),
        ("user2", "passwd2")
        ])
    def param(request):
        return request.param
    
    @pytest.fixture(autouse=True)
    def login(param):
        print("
    登录成功 %s %s" %param)
        yield
        print("
    退出成功 %s %s" %param)
    
    def test_api():
        assert 1 == 1
    
    '''
    pytest -s -v test_demo.py
    运行结果:
    test_demo.py::test_api[param0]
    登录成功 user1 passwd1
    PASSED
    退出成功 user1 passwd1
    
    test_demo.py::test_api[param1]
    登录成功 user2 passwd2
    PASSED
    退出成功 user2 passwd2
    '''

    assert

    assert "h" in "hello"
    assert 3==4
    assert 3!=4
    assert f()==4
    assert 5>6
    assert not xx
    assert {"0", "1", "2"} == {"0", "1", "2"}
  • 相关阅读:
    Dedecms5.7修改文章,不改变发布时间的方法
    dedecms列表页如何调用栏目关键词和描述
    DEDE内容页调用栏目的SEO标题、描述、关键字的方法
    织梦安装过后出现"...www/include/templets/default/index.htm Not Found!"
    DEDE无简略标题时显示完整标题
    Dede调用简略标题_简略标题标签(短标题)
    修改dede提示信息
    交叉栏目实现织梦首页分页
    织梦添加和调用自定义字段的方法
    织梦列表页和内容页调用缩略图的方法
  • 原文地址:https://www.cnblogs.com/belle-ls/p/12712462.html
Copyright © 2011-2022 走看看