• pytest单元测试框架fixture应用


    fixture非常有用,它们提供了固定的基准,因此测试可以被可靠地执行并产生一致的,可重复的结果。使用fixture可以设置服务,状态或其他操作环境。测试函数通过参数调用fixture方法。

    fixture比setup、teardown等方法更好

    1.fixture具有明确的名称,可以通过在测试功能,模块,类或整个项目中声明其使用来激活。

    2.fixture以模块的方式实现,因为每个fixture方法都会触发一个fixture功能,该功能本身可以使用其他fixture。

    3.fixture管理的范围从简单的单元测试到复杂的功能测试,可以根据配置和组件选项对fixture和测试进行参数化,或者在功能,类,模块或整个测试会话范围内重复使用fixture。

    下面应用理解

    使用测试程序

    /testapi/add/

    /testapi/minus/

    /testapi/chengfa/

    使用pytest测试类

    import pytest
    import requests
    import json
    import os
    import logging
    
    
    
    class TestMath(object):
        @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)])
        def testadd(self,a,b):
            '''测试加法程序'''
            headers = {
                'Content-Type': "application/json",
            }
            reqdata = {'a':a,'b':b}
            resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/add/', verify=False, headers=headers, json=reqdata)
    
            resp = json.loads(resp.text)
            assert resp['status']==1
            assert resp['data'] == a + b
            return resp['data']
    
        @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)])
        def testminus(self,a,b):
            '''测试加法程序'''
            headers = {
                'Content-Type': "application/json",
            }
            reqdata = {'a':a,'b':b}
            print(reqdata)
            resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/minus/', verify=False, headers=headers, json=reqdata)
            resp = json.loads(resp.text)
            print(resp)
            assert resp['status']==1
            assert resp['data'] == a - b
            return resp['data']
    
        @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)])
        def testchengfa(self,a,b):
            '''测试加法程序'''
            headers = {
                'Content-Type': "application/json",
            }
            reqdata = {'a':a,'b':b}
            print(reqdata)
            resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/chengfa/', verify=False, headers=headers, json=reqdata)
            # warnings.warn('这是个警告', category=None, stacklevel=1, source=None)
            resp = json.loads(resp.text)
            print(resp)
            assert resp['status']==1
            assert resp['data'] == a * b
            return resp['data']
    
    if __name__ == '__main__':
        pytest.main(['D:/PycharmProjects/untitled/test_math.py'])

    比如说要使用cookie,一般来说是需要初始化

    @pytest.fixture
    def mycookie(self):
    self.s = requests.session()
    c = requests.cookies.RequestsCookieJar()
    c.set('testcookies', 'XXXXXXX')
    self.s.cookies.update(c)
    return self.s

    测试方法中加入print(mycookie.cookies['testcookies'])

    运行看见能打印XXXXXXX

    conftest.py

    如果在实施测试期间您意识到要使用fixture到多个测试文件中的功能,则可以将其移至一个conftest.py文件中。测试中无需在所有文件导入这个fixture,它会被pytest自动发现。夹具功能的发现始于测试类,然后是测试模块,然后是 conftest.py文件,最后是内置插件和第三方插件。

    fixture作用范围

    夹具是在测试首次请求时创建的,并根据以下scope的值决定fixture何时被销毁:

    function:默认范围,fixture在单个测试对象运行结束时被销毁。

    class:在class中的最后一个测试对象运行完被销毁。

    module:在文件中的最后一个测试对象运行完被销毁。

    package:在包中的最后一个测试对象运行完被销毁。

    session:在整个测试阶段结束时被销毁。

    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"]

    test_order调用的fixture将按以下顺序实例化:

    s1:session是外面一层。

    m1:module是第二外面一层。

    a1:是一个autouse-fixture:它会在同一范围内的其它fixture之前被实例化。

    f3:被同级别调用的比调用者先实例化

    f1:同级别按排名顺序调用

    f2:同上

    使用fixture代替setup/teardown

    通过使用yield语句代替returnyield语句之后的所有代码都将用作teardown的功能。

    例如登录和退出

        @pytest.fixture(scope='module',autouse=True)
        def logginandout(self):
            print('登录成功')
            yield
            print('退出成功')
    test_math.py::TestMath::testadd[0.1-0.2] 登录成功
    ...
    FAILED退出成功

    改成scope='function'的话

    test_math.py::TestMath::testadd[0.1-0.2] 登录成功
    XXXXXXX
    PASSED退出成功
    
    test_math.py::TestMath::testadd[1-2] 登录成功
    XXXXXXX
    PASSED退出成功
    
    test_math.py::TestMath::testminus[0.1-0.2] 登录成功
    {'a': 0.1, 'b': 0.2}
    {'status': 1, 'message': '请求成功', 'data': -0.1}
    PASSED退出成功
    
    test_math.py::TestMath::testminus[1-2] 登录成功
    {'a': 1, 'b': 2}
    {'status': 1, 'message': '请求成功', 'data': -1}
    PASSED退出成功
    
    test_math.py::TestMath::testchengfa[0.1-0.2] 登录成功
    {'a': 0.1, 'b': 0.2}
    {'status': 1, 'message': '请求成功', 'data': 0.30000000000000004}
    FAILED退出成功
    
    test_math.py::TestMath::testchengfa[1-2] 登录成功
    {'a': 1, 'b': 2}
    {'status': 1, 'message': '请求成功', 'data': 3}
    FAILED退出成功

    fixture参数化

    可以对fixture功能进行参数设置,在这种情况下,它们将被多次调用,每次执行一组相关测试,即依赖于该fixture的测试夹具参数化有助于为组件编写详尽的功能测试,这些组件本身可以通过多种方式进行配置。

        @pytest.fixture(params= [(0.1,0.2),(1,2)],ids=['小数','整数'])
        def gettestdata(self,request):
            return request.param

    加入以上fixture并修改测试方法

        # @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)])
        def testadd(self,gettestdata,mycookie):
            '''测试加法程序'''
            print(mycookie.cookies['testcookies'])
            a,b = gettestdata[0],gettestdata[1]
            headers = {
                'Content-Type': "application/json",
            }
            reqdata = {'a':a,'b':b}
            resp = mycookie.request(method='POST', url='http://127.0.0.1:8000/testapi/add/', verify=False, headers=headers, json=reqdata)
            resp = json.loads(resp.text)
            assert resp['status']==1
            assert resp['data'] == a + b
            return resp['data']
    ...

    pytest.main(['-v',''])运行成功

    test_math.py::TestMath::testadd[小数] PASSED [ 16%]
    test_math.py::TestMath::testadd[整数] PASSED [ 33%]
    test_math.py::TestMath::testminus[小数] PASSED [ 50%]
    test_math.py::TestMath::testminus[整数] PASSED [ 66%]
    test_math.py::TestMath::testchengfa[小数] FAILED [ 83%]
    test_math.py::TestMath::testchengfa[整数] FAILED [100%]

    注意加入scope='class'顺序不同

    test_math.py::TestMath::testadd[小数] PASSED                             [ 16%]
    test_math.py::TestMath::testminus[小数] PASSED                           [ 33%]
    test_math.py::TestMath::testchengfa[小数] FAILED                         [ 50%]
    test_math.py::TestMath::testadd[整数] PASSED                             [ 66%]
    test_math.py::TestMath::testminus[整数] PASSED                           [ 83%]
    test_math.py::TestMath::testchengfa[整数] FAILED                         [100%]

    加入scope='function'

    test_math.py::TestMath::testadd[小数] PASSED                             [ 16%]
    test_math.py::TestMath::testadd[整数] PASSED                             [ 33%]
    test_math.py::TestMath::testminus[小数] PASSED                           [ 50%]
    test_math.py::TestMath::testminus[整数] PASSED                           [ 66%]
    test_math.py::TestMath::testchengfa[小数] FAILED                         [ 83%]
    test_math.py::TestMath::testchengfa[整数] FAILED                         [100%]

    使用usefixtures

    有时测试功能不需要直接访问fixture对象。使用@pytest.mark.usefixtures直接调用

    # conftest.py
    
    @pytest.fixture()
    def writelog(self):
        try:
            self.resultfile = open('testtext.txt', 'w+', encoding='utf-8')
        except Exception as e:
            pass
        if self.resultfile.writable():
            self.resultfile.writelines('测试开始!')
            self.resultfile.writelines('
    ')
            yield
            self.resultfile.writelines('测试结束!')
            self.resultfile.writelines('
    ')
        self.resultfile.close()

        测试类之前加上@pytest.mark.usefixtures('writelog')就能写入文件

    测试开始!
    测试结束!

    可以指定多个灯具

    可以将项目中所有测试所需的灯具放入ini文件中

    # content of pytest.ini
    [pytest]
    usefixtures = writelog

    使用fixture读取excel文件参数进行测试

    加入Excel进行测试是比较常规的参数化做法

    首先加入读取excel方法

        def getdata(cls):
            file = xlrd.open_workbook('test.xlsx')
            sheet = file.sheet_by_index(0)
            rownum = file.sheet_by_index(0).nrows
            ablist = []
            for row in range(rownum):
                if row:
                    rowvalue = sheet.row_values(row)
                    ablist.append((rowvalue[2], rowvalue[3]))
            return ablist
            #ablist = [(0.1, 0.2), (1.0, 2.0), (0.1, 2.0)]

    然后用fixture调用此方法

        @pytest.fixture(params= getdata('TestMath'))
        def gettestdata(self,request):
            print('参数' + str(request.param))
            return request.param

    查看运行结果

    test_math.py::TestMath::testadd[gettestdata0] PASSED                     [ 11%]
    test_math.py::TestMath::testadd[gettestdata1] PASSED                     [ 22%]
    test_math.py::TestMath::testadd[gettestdata2] PASSED                     [ 33%]
    test_math.py::TestMath::testminus[gettestdata0] PASSED                   [ 44%]
    test_math.py::TestMath::testminus[gettestdata1] PASSED                   [ 55%]
    test_math.py::TestMath::testminus[gettestdata2] PASSED                   [ 66%]
    test_math.py::TestMath::testchengfa[gettestdata0] FAILED                 [ 77%]
    test_math.py::TestMath::testchengfa[gettestdata1] FAILED                 [ 88%]
    test_math.py::TestMath::testchengfa[gettestdata2] FAILED                 [100%]

    标签不好看,添加一下ids

        def getdata(cls):
            file = xlrd.open_workbook('test.xlsx')
            sheet = file.sheet_by_index(0)
            rownum = file.sheet_by_index(0).nrows
            title = []
            ablist = []
            for row in range(rownum):
                if row:
                    rowvalue = sheet.row_values(row)
                    title.append(rowvalue[1])
                    ablist.append((rowvalue[2], rowvalue[3]))
            return (title,ablist)

    修改为@pytest.fixture(params= getdata('TestMath')[1],ids=getdata('TestMath')[0],在运行看运行结果

    test_math.py::TestMath::testadd[两个小数] PASSED                         [ 11%]
    test_math.py::TestMath::testadd[两个整数] PASSED                         [ 22%]
    test_math.py::TestMath::testadd[小数和整数] PASSED                       [ 33%]
    test_math.py::TestMath::testminus[两个小数] PASSED                       [ 44%]
    test_math.py::TestMath::testminus[两个整数] PASSED                       [ 55%]
    test_math.py::TestMath::testminus[小数和整数] PASSED                     [ 66%]
    test_math.py::TestMath::testchengfa[两个小数] FAILED                     [ 77%]
    test_math.py::TestMath::testchengfa[两个整数] FAILED                     [ 88%]
    test_math.py::TestMath::testchengfa[小数和整数] FAILED                   [100%]

    漂亮很多

    添加参数scope='module'运行查看结果,运行顺序有变化,说明默认为scope='function',pytest框架寻找每个方法调用参数,scope='class',pytest框架寻找每个类调用参数,以此类推

    test_math.py::TestMath::testadd[两个小数] PASSED                         [ 11%]
    test_math.py::TestMath::testminus[两个小数] PASSED                       [ 22%]
    test_math.py::TestMath::testchengfa[两个小数] FAILED                     [ 33%]
    test_math.py::TestMath::testadd[两个整数] PASSED                         [ 44%]
    test_math.py::TestMath::testminus[两个整数] PASSED                       [ 55%]
    test_math.py::TestMath::testchengfa[两个整数] FAILED                     [ 66%]
    test_math.py::TestMath::testadd[小数和整数] PASSED                       [ 77%]
    test_math.py::TestMath::testminus[小数和整数] PASSED                     [ 88%]
    test_math.py::TestMath::testchengfa[小数和整数] FAILED                   [100%]
  • 相关阅读:
    hdoj1587
    欧拉定理及其应用
    hdoj1571
    hdoj1050
    POJ推荐50题
    poj2593
    hdoj1286
    hdoj1215七夕节
    我的Linux软件
    ACM题目推荐--《算法艺术与信息学竞赛》(转)
  • 原文地址:https://www.cnblogs.com/zerotest/p/13583440.html
Copyright © 2020-2023  润新知