Pytest单元测试框架

 
Pytest单元测试框架

Pytest框架介绍

pytest是Python的一种单元测试框架(python3之前是自带,3之后需要手动安装),与之自带的unittest相似,但是相比unittest,pytest更加高效以及简介,且向下兼容unittest

命名规则

  • 测试文件应当命名为test_**.py或者**_test.py
  • 测试函数,测试类方法应当名为为test开头
  • 测试类应当命名为Test开头
  • 所有的包pakege必须要有__init__.py文件

属性说明

用例跳过

  • 用例跳过@pytest.mark.skip()(对未完成的用例跳过,不执行)可以添加在类以及function上,则对应的用例不再执行

参数化

  • 参数化@pytest.mark.parametrize

固件

  • 固件(Fixture)本身是一些函数,pytest会在执行用例之前或者之后运行这些函数,最常见可以用它来初始连接数据库以及关闭数据库。可以在不同的目录层级定义conftest.py,其作用域为其所在的目录和子目录
 
import pytest
@pytest.fixture()
def dbconn():
    print ('conn success')
    yield 
    print('conn close')
def test_case(dbconn):
    print('case1')
    assert(1 == 1)
 
  • 固件作用域,通过scop参数来声明作用域
    • function,函数级,每个测试函数都会执行一次固件,不声明参数时默认为此作用域。
    • Class,类级别,每个测试类执行一次,所有方法都可以使用。
      • 使用pytest.mark.usefixtures()进行标记(对函数同样适用),结果如上
      • 如果一个方法或者一个class用例想要同时调用多个fixture,可以使用@pytest.mark.usefixture()进行叠加。注意叠加顺序,先执行的放底层,后执行的放上层。
import pytest
@pytest.fixture(scop = 'class')
def dbconn():
    print ('conn success')
    yield 
    print('conn close')
@pytest.mark.usefixtures()
class TestCase(object):
def test_case():
    print('case1')
    assert(1 == 1)
  • Module,模块级,每个模块执行一次,模块内.py的函数和方法都可使用执行,
  • session,会话级,一次测试只执行一次,所有被找到的函数和方法都可用。
  • 固件自动执行(定义时指定autouse为True,默认为False)
import time
#获取每个函数的运行时间
data_format = '%Y-%m-%d %H:%M:%S'   
@pytest.fixture(autouse=True)  #为true时自动执行,默认为False
def time_function_scope():   
    start_time = time.time()
    yield
    print('Time cost:{:.3f}s'.format(time.time() - start_time))

def_test_auto():
    print('我是自动执行')
    time.sleep(2)
    assert (1 + 1 == 2)
执行结果如下:
  • 固件重命名
import pytest
@pytest.fixture(name='age')
def average_age():
    return 18
def test_age(age):
    print(age)
    assert age == 20
  • 固件的参数化(固件本身也是函数),可对不同环境下相同操作的一些场景进行处理
import pytest
@pytest.fixture(params=[
    ('redis','6369'),
    ('elasticsearch', '9200')])                                                
def param(request): #固件参数化需使用pytest内置固件request,并通过request.param获取参数。
    return request.param    
@pytest.fixture(autouse=True)
def db(param):
    print('success connet: %s:%s'%param)
    yield
    print('success to close:%s:%s'%param)
def test_conn():            #2条用例(一组输出redise,一组输出elasticsearch)
    print('这是用例1')
    assert 1==1   

 

命令行参数

  • Pytest -v 输出用例执行信息,如文件名、用例名
  • Pytest -s 输出调试信息,如print打印
  • pytest -m 用于执行特定的用例,使用mark标记
  • Python -x 遇到错误则退出执行

生成器

如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
g = (x * x for x in range(10))
定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator。generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield 3
    print('step 3')
    yield 5
t = odd()
next(t)   #打印“step 1”
next(t)   #打印“step 2”
next(t)   #打印“step 3”
#错误写法,没有创建生成器对象,那么多次调用都会创建新的独立的generator对像,所以一直输出step1
next(odd())  #打印“step 1”
next(odd())  #打印“step 1”
next(obb())  #打印“step 1”
ps:生成器一定是迭代器,但是迭代器不一定是生成器。
带参数返回:
 
def my_generator(n):
    for i in range(n):
        temp = yield i
        print(f'我是{temp}')

g = my_generator(5)

print(next(g))  # 输出0
print(next(g))  # 输出1
g.send(100)  # 本来输出2,但是传入新的值100,改为输出100
print(next(g))  # 输出3
生成器实现协程
协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。其实就是通过一个线程实现代码块相互切换执行从而在单个线程下完成多个任务。
  • 通过yield实现协程
def test1():
    yield 'test1'
    yield from test2()
    yield 'test2'
def test2():
    yield 'test3'
    yield 'test4'

if __name__ == '__main__':
    a = test1()
    for i in a:
        print(i) 
  • 通过asyncio以及await关键字实现
import asyncio
async def test1():
    print('test1')
    await asyncio.sleep(2)
    print('test2')
async def test2():
    print('test3')
    await asyncio.sleep(2)
    print('test4')

if __name__ == '__main__':
    tasks = [
        asyncio.ensure_future(test1()),
        asyncio.ensure_future(test2())
    ]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
  • 第三方网络库greenlet实现
from greenlet._greenlet import greenlet
def test1():
    print('test1')
    b.switch()
    print('test2')
def test2():
    print('test3')
    a.switch()   #切换到test1函数并从上一次开始的地方开始执行
    print('test4')

if __name__ == '__main__':
    a = greenlet(test1)
    b = greenlet(test2)
    a.switch()
    b.switch()
通过装饰器实现
#Python3.4以后都用第三种方式实现了
@asyncio.coroutine
def test1():
    print('test1')
    yield from asyncio.sleep(2)
    print('test2')
@asyncio.coroutine
def test2():
    print('test3')
    yield from asyncio.sleep(2)
    print('test4')

if __name__ == '__main__':
    tasks = [
        asyncio.ensure_future(test1()),
        asyncio.ensure_future(test2())
    ]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))

迭代器

可迭代对象Iterable:可直接作用于for循环的对象,如list,dict,tuple,str,set
迭代器:可以被next()函数调用且不断返回下一个值的对象就是迭代器Iterator
for循环本身就是不断通过调用next()方法实现的,for循环做了哪些事:
  • 通过iter()方法返回一个迭代器对象
  • 通过next()方法返回迭代的值,
  • 处理迭代完成后的异常
for i in [1,2,3,4,5]:
    print(i)
#实现原理等价于:
a = iter([1,2,3,4,5])  #相当于for循环里的第1条
while True:
    try:
        print(next(a))  #相当于for循环处理的第2条
    except StopIteration:
        break   # 遇到StopIteration就退出循环,相当于for循环处理的第3条

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2022-08-24 20:59  止无~  阅读(154)  评论(0)    收藏  举报