zoukankan      html  css  js  c++  java
  • pytest使用

    编写规范:
    • 测试⽂件以 test_ 开头(以 _test 结尾也可以)!
    • 测试类以 Test 开头,并且不能带有 __init__ ⽅法!
    • 测试函数以 test_ 开头

     1)创建python文件

    def add(x, y):
        return x + y
    
    def test_add():
        print("---add1----")
        assert add(1, 2) == 3
    
    
    def test_add2():
        print("---add2----")
        assert add(1.2, 3.1) == 42.3

    2)控制台运行

    pytest test_p02.py

    D:javaideaworkSpaces	estpythonWebsimple	est03>pytest test_p02.py
    ====================================================================== test session starts =======================================================================
    platform win32 -- Python 3.8.1, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
    rootdir: D:javaideaworkSpaces	estpythonWebsimple	est03
    plugins: html-3.1.1, metadata-1.11.0, remotedata-0.3.2
    collected 2 items                                                                                                                                                 
    
    test_p02.py .F                                                                                                                                              [100%]
    
    ============================================================================ FAILURES ============================================================================
    ___________________________________________________________________________ test_add2 ____________________________________________________________________________
    
        def test_add2():
            print("---add2----")
    >       assert add(1.2, 3.1) == 42.3
    E       assert 4.3 == 42.3
    E        +  where 4.3 = add(1.2, 3.1)
    
    test_p02.py:11: AssertionError
    ---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------
    ---add2----
    ==================================================================== short test summary info =====================================================================
    FAILED test_p02.py::test_add2 - assert 4.3 == 42.3
    ================================================================== 1 failed, 1 passed in 0.63s ===================================================================

    2、参数化

    import pytest
    
    @pytest.mark.parametrize("x,y",[
        (3+3,8),
        (9-1,8),
        (2*4,8),
        (32/4,8)
    ])
    def test_math(x,y):
        assert x==y

    运行   pytest test_p03.py

    D:javaideaworkSpaces	estpythonWebsimple	est03>pytest test_p03.py
    ====================================================================== test session starts =======================================================================
    platform win32 -- Python 3.8.1, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
    rootdir: D:javaideaworkSpaces	estpythonWebsimple	est03
    plugins: html-3.1.1, metadata-1.11.0, remotedata-0.3.2
    collected 4 items                                                                                                                                                 
    
    test_p03.py F...                                                                                                                                            [100%]
    
    ============================================================================ FAILURES ============================================================================
    _________________________________________________________________________ test_math[6-8] _________________________________________________________________________
    
    x = 6, y = 8
    
        @pytest.mark.parametrize("x,y",[
            (3+3,8),
            (9-1,8),
            (2*4,8),
            (32/4,8)
        ])
        def test_math(x,y):
    >       assert x==y
    E       assert 6 == 8
    
    test_p03.py:10: AssertionError
    ==================================================================== short test summary info =====================================================================
    FAILED test_p03.py::test_math[6-8] - assert 6 == 8
    ================================================================== 1 failed, 3 passed in 0.60s ===================================================================

    单个参数,随机数

    import pytest
    import random
    
    @pytest.mark.parametrize("x",[(1),(3),(5),(7)])
    def test_random(x):
        ran = random.randrange(1,8)
        assert x == ran

    运行

     参数化方式二:

    import pytest
    
    
    test_user_data=['zs','123456']
    
    
    @pytest.fixture(scope="module")
    def login_r(request):
        user = request.param+'___00加工00'
        print('
    登录用户%s'%user)
        return user
    
    #indirect=True,把login_r当做函数来执行
    @pytest.mark.parametrize("login_r",test_user_data,indirect=True)
    def test_login(login_r):
        a=login_r
        print('测试用例返回:%s'%a)
        assert a != ""

    执行原理:将参数test_user_data传入方法login_r()进行加工,加工完成后,返回到test_login

    结果

    参数化--字典格式数据

    import pytest
    
    
    test_user_data=[{'user':'zs','pwd':'123456'},
                    {'user':'ls','pwd':'456'},
                    {'user':'ww','pwd':'16'},
                    {'user':'sa','pwd':''}
                    ]
    
    
    @pytest.fixture(scope="module")
    def login_r(request):
        user = request.param['user']
        pwd = request.param['pwd']
        print('
    登录用户:%s,pwd:%s'%(user,pwd))
        if pwd:
            return True
        else:
            return False
    
    
    #indirect=True 把login_r当做函数执行
    @pytest.mark.parametrize("login_r",test_user_data,indirect=True)
    def test_login(login_r):
        a=login_r
        print('测试用例返回:%s'%a)
        assert a ,'登录失败,密码为空'

    结果

    collecting ... 
    登录用户:zs,pwd:123456
    测试用例返回:True
    
     test_p02.py ✓                                                                                                                                       25% ██▌
       
    登录用户:ls,pwd:456
    测试用例返回:True
     test_p02.py ✓✓                                                                                                                                      50% █████
         
    登录用户:ww,pwd:16
    测试用例返回:True
     test_p02.py ✓✓✓                                                                                                                                     75% █████
    ██▌  
    登录用户:sa,pwd:
    测试用例返回:False
    
    
    ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― test_login[login_r3]
    ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
    
    login_r = False
    
        @pytest.mark.parametrize("login_r",test_user_data,indirect=True)
        def test_login(login_r):
            a=login_r
            print('测试用例返回:%s'%a)
    >       assert a ,'登录失败,密码为空'
    E       AssertionError: 登录失败,密码为空
    E       assert False
    
    test_p02.py:27: AssertionError
    
     test_p02.py ⨯                                                                                                                                      100% █████
    █████
    ==================================================================== short test summary info =====================================================================
    FAILED test_p02.py::test_login[login_r3] - AssertionError: 登录失败,密码为空
    
    Results (0.60s):
           3 passed
           1 failed
             - test_p02.py:23 test_login[login_r3]
    View Code

    参数化---多个字典

    import pytest
    
    
    test_user_data=[{'user':'zs','pwd':'123456'},
                    {'user':'ls','pwd':'456'},
                    {'user':'ww','pwd':'16'}
                    ]
    
    test_user_data2 = [{"q": "中国平安", "count": 3, "page": 1},
                       {"q": "阿里巴巴", "count": 2, "page": 2},
                       {"q": "pdd", "count": 3, "page": 1}]
    
    @pytest.fixture(scope="module")
    def login_r(request):
        #数据加工
        user = request.param['user']
        pwd = request.param['pwd']
        print('
    登录用户:%s,pwd:%s'%(user,pwd))
        return request.param
    
    @pytest.fixture(scope="module")
    def query_param(request):
        q = request.param['q']
        count = request.param['count']
        page = request.param['page']
        print("查询的搜索词:%s" % q)
        return request.param
    
    
    # 这是pytest的参数化数据驱动,indeirect=True 是把login_r当作函数去执行
    # 从下往上执行
    # 两个数据进行组合测试有3*3个测试用例执行(test_user_data1的个数*test_user_data2的个数)
    @pytest.mark.parametrize("query_param",test_user_data2,indirect=True)
    @pytest.mark.parametrize("login_r",test_user_data,indirect=True)
    def test_login(login_r,query_param):
        values = login_r.values()
        print('用户名 %s ,密码:%s'%(list(values)[0],list(values)[1]))
        u_values =  query_param.values()
        print('名称:%s,总共:%s页,当前:%s页' %(list(u_values)[0],list(u_values)[1],list(u_values)[2]))

    结果

    D:javaideaworkSpaces	estpythonWebpytest_simple	est06>pytest -s  test_p03.py
    Test session starts (platform: win32, Python 3.8.1, pytest 6.2.1, pytest-sugar 0.9.4)
    rootdir: D:javaideaworkSpaces	estpythonWebpytest_simple	est06
    plugins: assume-2.4.2, html-3.1.1, metadata-1.11.0, ordering-0.6, remotedata-0.3.2, rerunfailures-9.1.1, sugar-0.9.4
    collecting ... 
    登录用户:zs,pwd:123456
    查询的搜索词:中国平安
    用户名 zs ,密码:123456
    名称:中国平安,总共:3页,当前:1页
    
     test_p03.py ✓                                                                                                                                       11% █▎
      查询的搜索词:阿里巴巴
    用户名 zs ,密码:123456
    名称:阿里巴巴,总共:2页,当前:2页
     test_p03.py ✓✓                                                                                                                                      22% ██▎
       
    登录用户:ls,pwd:456
    用户名 ls ,密码:456
    名称:阿里巴巴,总共:2页,当前:2页
     test_p03.py ✓✓✓                                                                                                                                     33% ███▍
        查询的搜索词:中国平安
    用户名 ls ,密码:456
    名称:中国平安,总共:3页,当前:1页
     test_p03.py ✓✓✓✓                                                                                                                                    44% ████▌
         查询的搜索词:pdd
    用户名 ls ,密码:456
    名称:pdd,总共:3页,当前:1页
     test_p03.py ✓✓✓✓✓                                                                                                                                   56% █████
    ▋    
    登录用户:zs,pwd:123456
    用户名 zs ,密码:123456
    名称:pdd,总共:3页,当前:1页
     test_p03.py ✓✓✓✓✓✓                                                                                                                                  67% █████
    █▋   
    登录用户:ww,pwd:16
    用户名 ww ,密码:16
    名称:pdd,总共:3页,当前:1页
     test_p03.py ✓✓✓✓✓✓✓                                                                                                                                 78% █████
    ██▊  查询的搜索词:阿里巴巴
    用户名 ww ,密码:16
    名称:阿里巴巴,总共:2页,当前:2页
     test_p03.py ✓✓✓✓✓✓✓✓                                                                                                                                89% █████
    ███▉ 查询的搜索词:中国平安
    用户名 ww ,密码:16
    名称:中国平安,总共:3页,当前:1页
     test_p03.py ✓✓✓✓✓✓✓✓✓                                                                                                                              100% █████
    █████
    
    Results (0.23s):
           9 passed
    View Code

    3、运行进度条

    安装 :  pip install pytest-sugar

     4、assume

    pip install assume

    assert断言失败之后,后面的断言也不会执行,包括正常的代码

    assume即使断言失败,后面的断言还是会继续执行,这有助于我们分析和查看到底一共有哪些断言是失败的,直接用assert更高效

    import pytest
    
    def test_assume():
        pytest.assume(1 == 2)
        pytest.assume(2 == 2)
        pytest.assume(3==2)

     5、rerunfailures

    在web、APP⾃动化测试中, 经常出现超时导致测试失败,所以需要重新运行

    pip install pytest-rerunfailures

    测试代码

    import random
    def add(x,y):
        return x+y
    
    def test_add():
        ran=random.randint(1,10)
        assert add(1,3) == ran

    运行结果

    pytest --reruns 8 test_p07.py

    设置最大重新运行测试为8。

    rerun  5次,总共运行6次,第六次时测试通过,不再运行。

     6、Pytest-ordering

    使得测试方法按照指定顺序运行

    pip install pytest-ordering

    代码

    import pytest
    
    value = 0
    @pytest.mark.run(order=2)
    def test_add():
        print("---add        [order=2]       ---")
        assert value == 10
    
    @pytest.mark.run(order=1)
    def test_sub():
        print("--sub     [order=1]---")
        global value
        value = 10
        assert value == 10

    7、冒泡排序

    def bubbleSort(arr):
        n = len(arr)
        # 遍历所有数组元素
        for i in range(n):
            for j in range(0, n-i-1):
                if arr[j] > arr[j+1] :
                    arr[j], arr[j+1] = arr[j+1], arr[j]
    
    arr = [64, 34, 25, 12, 22, 11, 90]
    bubbleSort(arr)
    print ("排序后的数组:")
    for i in range(len(arr)):
        print ("%d" %arr[i])

    8、函数级别

     代码

    import pytest
    
    
    def setup_module():
        print('整个模块.py开始')
    
    
    def teardown_module():
        print('整个模块.py结束')
    
    
    def setup_function():
        print('不在类中的函数前')
    
    
    def teardown_function():
        print('不在类中的函数后')
    
    
    def test_no_01():
        print('不在类中的方法1')
    
    
    def test_no_02():
        print('不在类中的方法2')
    
    
    class TestClass:
        def setup_class(self) -> None:
            print('类前面')
    
        def teardown_class(self):
            print('类之后')
    
        def setup_method(self):
            print('方法前')
    
        def teardown_method(self):
            print('方法后')
    
        def test_one(self):
            print('one')
    
    
        def test_two(self):
            print('two')
    
    
    
    if __name__ == '__main__':
        pytest.main(["-s", "test_p01.py"])

    结果

    模块(setup_module) --》不在类中的方法(setup_function)--》具体方法,不再在中--》类(setup_class)--》类中的方法(setup_method)--》具体方法

     9、运行方式

     10.pytest-fixture

     类似于setup,在一个方法之前运行

    在加入购物车、购买之前先登录,scope="function"

    实现方式一:直接将fixture的引用写在方法的参数,如  test_shoppingChart(login):

    import pytest
    
    
    @pytest.fixture()
    def login():
        print('登录')
    
    
    def test_select():
        print('搜索')
    
    
    def test_shoppingChart(login):
        print('加入购物车')
    
    
    def test_pay(login):
        print('购买')
    
    
    if __name__ == '__main__':
        pytest.main(['-s', 'test_p02.py'])

    结果

     实现方式二@pytest.mark.usefixtures("login")

    import pytest
    
    
    @pytest.fixture()
    def login():
        print('登录')
    
    
    def test_select():
        print('搜索')
    
    
    @pytest.mark.usefixtures("login")
    def test_shoppingChart():
        print('加入购物车')
    
    
    @pytest.mark.usefixtures("login")
    def test_pay():
        print('购买')

    将公共模块放入共享文件

     查找顺序:先在本文件中查找,如果没找到,在从conftest.py中查找

    例如:刚才的例子,将login放进conftest.py中,进行数据共享

     

     运行

     11、yield

    使用场景:在执行方法前要执行依赖的模块,在执行方法后要销毁清除数据,范围模块级别  scope="module"

    import pytest
    
    
    @pytest.fixture(scope="module")
    def openBrower():
        print('打开浏览器')
    
        yield
        print('关闭浏览器')
    
    
    def test_search(openBrower):
        print('搜索:shell')
    
    
    def test_look(openBrower):
        print('查看浏览器返回结果')
    
    
    def test_searchAagin(openBrower):
        print('搜索:bash')

    结果

    如上述代码如果每个方法的调用都通过参数指定,会比较麻烦而且不利于扩展,可以选择

    使用fixture中参数autouse=True,替换掉方法参数

    
    
    import pytest


    @pytest.fixture(scope="module",autouse=True)
    def openBrower():
    print('打开浏览器')

    yield
    print('关闭浏览器')


    def test_search():
    print('搜索:shell')


    def test_look():
    print('查看浏览器返回结果')


    def test_searchAagin():
    print('搜索:bash')

    综合:@pytest.fixture(scope="module", autouse=True),@pytest.mark.usefixtures("login")一起使用

    import pytest
    
    
    @pytest.fixture(scope="module",autouse=True)
    def openBrower():
        print('打开浏览器')
        print('进入网页版淘宝')
        yield
        print('关闭浏览器')
    
    
    @pytest.fixture(scope="function")
    def login():
        print('登录')
    
    
    #搜索:不用登陆
    def test_search():
        print('搜索:包包')
    
    
    #加入购物车之前,先登录
    @pytest.mark.usefixtures("login")
    def test_chart():
        print('加入购物车')
    
    
    #下单之前,先登录
    @pytest.mark.usefixtures("login")
    def test_pay():
        print('下单')

    结果

     12、skip与xfail

     

     代码

    import pytest
    import sys
    import time
    # scope=function
    '''
    skip
    '''
    
    def test_soso(login):
        print('case1: 登录后执行搜索')
        assert 1 == 1
        assert {'name': 'linda', 'age': 18} == {'name': 'linda', 'age': 188}
        a = 'hello'
        age = 35
        assert a in 'hello world'
        assert 20 < age < 80
    
    
    def f():
        return 3
    
    
    @pytest.mark.skip
    def test_cakan():
        print('case2:不登陆,就看是否执行')
        assert f() == 4
    
    
    environment = 'android'
    
    
    @pytest.mark.skipif('environment=="android"', reason='android平台没有这个功能')
    def test_cart_3(login):
        print('case3,登陆,点击苹果图标')
    
    
    @pytest.mark.skipif(sys.platform =='win32', reason='不在windows下运行')
    @pytest.mark.skipif(sys.version_info < (3,6), reason='3.6版本行,您需要更高版本')
    def test_cart(login):
        print('case3,登陆,点击苹果图标,3.6以下版本无法执行')
    
    
    @pytest.mark.xfail
    def test_xfail():
        print(broken_fixture())
    
    
    def broken_fixture():
        raise Exception("Sorry, it's 中断异常.")

    运行

     

     运行时指定平台

     代码

    import pytest
    
    
    @pytest.mark.webtest
    def test_send_http():
        pass
    
    
    @pytest.mark.apptest
    def test_devide():
        pass
    
    
    @pytest.mark.android
    def test_search():
        pass
    
    
    @pytest.mark.ios
    def test_add():
        pass
    
    
    def test_plus():
        pass
    
    
    if __name__ == '__main__':
        pytest.main()

    运行

    pytest -s test_p05.py -m "ios"

     13、通过文件名、类名、方法名及组合调动部分用例执行

     pytest-s -v 文件名::类名::方法名

    pytest -k "类名 and  方法名"

     

    14、用例出错时停止

     15、多线程并行与分布式执行

    16、pytest-html生成测试报告

    pip install pytest-html

    pytest -v -s --html=report.html -- self-contained-html

    17、allure生成测试报告

    1)下载

    https://github.com/allure-framework/allure2/releases

    2)安装

    解压到任意⽬录下,把 allure-2.7.0in, 加⼊到环境path⾥⾯

    命令⾏下执⾏: pip install allure-pytest

    命令⾏下执⾏:allure --version  如果出现版本号就代表安装成功

    3)使用

    方式一:

    在测试期间收集结果

    pytest -s -q --alluredir=./result/

    从结果生成报告,这是一个启动Tomcat的服务,只生成报告.clean用于覆盖路径

    allure generate ./result/ -o ./report/ --clean

    打开报告

    allure open -h 127.0.0.1 -p 8883 ./report/

    方式二

    在测试期间收集结果

    pytest -s -q --alluredir=./result/

    测试完成后查看实际报告,在线看报告

    allure serve ./result/

     18、在测试报告中增加‘测试功能、子功能/场景、测试步骤、附加信息’等信息

    利用@Feature,story,step,@attach

    步骤:

    1)import allure

    2)在功能上加@allure.feature('功能名称')

    3)在子功能上加@allure.story('子功能名称')

    4)在步骤上加@allure.step('步骤细节)

    5)需要附加信息,可以是数据、文本、图片、网页;在要附加的地方加@allure.attach('具体文本信息')

    6)如果只测试购物车功能可以限制顾虑:pytest 文件名 --allure_features='购物车功能' --allure_stories='加入购物车' 

     

     19、按照重要级别进行一定范围测试

    lure.severity("critical")               # 优先级,包含blocker, critical, 
    def test_case_19688(para_one, para_two):
    if __name__ == '__main__':
        # 执行,指定执行测试模块_demo1, 测试模块_demo2两个模块,同时指定执行的用例优先级为critical,blocker
        pytest.main(['--allure_feature=测试功能_demo1', '--allure_stories=测试模块_demo2', '--allure_severities=critical, blocker'])

    完整代码

    import pytest
    import allure
    import logging
    
    '''
    test_allure_all.py
    '''
    # 测试函数
    @allure.step("测试步骤1:字符串相加:{0},{1}")     # 测试步骤,可通过format机制自动获取函数参数
    def str_add(str1, str2):
        print("hello",str1,str2)
        if not isinstance(str1, str):
            return "%s 不是字符串" % str1
        if not isinstance(str2, str):
            return "%s 不是字符串" % str2
        return str1 + str2
    
    
    @allure.description("测试相加的各种情况")
    @allure.severity("critical")               # 优先级,包含blocker, critical, normal, minor, trivial 几个不同的等级
    @allure.feature("测试功能_demo1")           # 功能块,feature功能分块时比story大,即同时存在feature和story时,feature为父节点
    @allure.story("测试模块_demo2")             # 功能块,具有相同feature或story的用例将规整到相同模块下,执行时可用于筛选
    # @allure.issue("BUG号:123")                 # BUG编号,关联标识已有的问题,可为一个url链接地址
    @allure.issue("http://www.jira.com/id=19688")   # BUG编号,关联标识已有的问题,可为一个url链接地址
    # @allure.testcase("用例名:测试字符串相等")      # 用例标识,关联标识用例,可为一个url链接地址
    @allure.testcase("http://www.testlink.com/id=19688")
    @pytest.mark.parametrize("para_one, para_two",              # 用例参数
                             [("hello world", "hello world"),   # 用例参数的参数化数据
                              ('4', '54'),
                              ("我不是超人", "我是超人"),
                              ("888", "我是超人")],
                             ids=["letter",          # 对应用例参数化数据的用例名
                                  "decimal123",
                                  "unicode",
                                  "mix"])
    def test_case_19688(para_one, para_two):
        """用例描述:测试字符串相等
        :param para_one: 参数1
        :param para_two: 参数2
        """
        logging.info("这是测试的信息,在log中输出")
        # 获取参数
        paras = vars()
        # 关联的资料信息, 可在报告中记录保存必要的相关信息
        allure.attach("用例参数", "{0}".format(paras))
        # 调用测试函数
        res = str_add(para_one, para_two)
        # 对必要的测试中间结果数据做备份
        allure.attach("str_add返回结果", "{0}".format(res))
        # 测试步骤,对必要的测试过程加以说明
        with allure.step("测试步骤2,结果校验 {0} == {1}".format(res, para_one+para_two)):
            allure.attach('<html><head></head><body> 附加网页看效果 </body></html>', '这是错误页的结果信息', allure.attachment_type.HTML)
            assert res == para_one+para_two, res
    
    
    if __name__ == '__main__':
        # 执行,指定执行测试模块_demo1, 测试模块_demo2两个模块,同时指定执行的用例优先级为critical,blocker
        pytest.main(['--allure_feature=测试功能_demo1', '--allure_stories=测试模块_demo2', '--allure_severities=critical, blocker'])
    View Code

     20、前端自动化测试截图

     完整代码

    import allure
    from selenium import webdriver
    import time
    import pytest
    
    '''
    test_sele_allure.py
    '''
    
    @allure.testcase("https://www.baidu.com的搜索功能")
    @pytest.mark.parametrize('test_data1', ['allure', 'pytest', 'unittest'])
    def test_steps_demo(test_data1):
        with allure.step('step one:打开浏览器输入百度网址'):
            driver = webdriver.Chrome()
            driver.get('https://www.baidu.com')
        with allure.step('step two:在搜索栏输入allure,并点击百度一下'):
    
            driver.find_element_by_id('kw').send_keys(test_data1)
            time.sleep(1)
            driver.find_element_by_id('su').click()
            time.sleep(1)
        with allure.step('step three:截图保存到项目中'):
    
            driver.save_screenshot("./result/b.png")
            # f = open('./result/b.png', 'rb').read()
            allure.attach.file("./result/b.png", attachment_type=allure.attachment_type.PNG)
            allure.attach('<head></head><body> 首页</body>', 'Attach with HTML type',
                          allure.attachment_type.HTML)
    
        with allure.step('step four:关闭浏览器,退出'):
            driver.quit()
    View Code

    结果

    分布式测试插件:https://github.com/pytest-dev/pytest-xdist

     https://github.com/linda883/
  • 相关阅读:
    hihocoder #1138 : Islands Travel
    关于c中的inline
    LUOGU P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm
    LUOGU P1908 逆序对
    归并排序 (模板)
    tyvj 1864 守卫者的挑战
    loj #10001. 「一本通 1.1 例 2」种树
    bzoj 1026: [SCOI2009]windy数
    BZOJ 4521: [Cqoi2016]手机号码
    LUOGU 3089 后缀排序(模板)
  • 原文地址:https://www.cnblogs.com/ychun/p/14372834.html
Copyright © 2011-2022 走看看