pytest单元测试框架fixture应用

fixture很是有用,它们提供了固定的基准,所以测试能够被可靠地执行并产生一致的,可重复的结果。使用fixture能够设置服务,状态或其余操做环境。测试函数经过参数调用fixture方法。json

fixture比setup、teardown等方法更好api

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

2.fixture以模块的方式实现,由于每一个fixture方法都会触发一个fixture功能,该功能自己可使用其余fixture。session

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

 

下面应用理解框架

使用测试程序函数

/testapi/add/单元测试

/testapi/minus/测试

/testapi/chengfa/url

 

使用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('\n')
        yield
        self.resultfile.writelines('测试结束!')
        self.resultfile.writelines('\n')
    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%]