pytest框架

目录

一、简介

1.1 pytest单元测试框架

1.2 单元测试框架和自动化测试框架有什么关系

1.3 pytest简介

1.4 pytest安装

1.4.1 pycharm中插件安装:

1.4.2 dos(cmd)窗口安装:

1.5 使用pytest,默认的测试用例的规则

二、基本使用

2.1 创建测试用例

2.1.1简单的函数

2.1.2 编写测试用例类

2.2 执行测试用例

2.2.1 通过pytest命令执行测试用例

2.2.2 通过Python代码执行(主函数)

2.2.3 参数详解

2.2.4 执行顺序

2.3 断言

2.4 前后置断言

2.4.1 经典的xunit风格

2.4.2 unittest风格

2.4.3 @pytest.fixture

​编辑 ​编辑

2.5 参数化

2.5.1 写法一

2.5.2 写法二

2.6 夹具别名

三、生成报告

3.1 生成测试报告

3.2 生成allure报告

3.2.1 下载、解压、配置path路径

3.2.2 加入命令生产json格式的临时报告

3.2.3 生成allure报告

四、pytest.ini配置文件

4.1存放位置

4.2 编码格式

4.3 作用

4.4 规则明细

五、分组执行(冒烟、分模块执行,分接口和web执行)

六、pytets跳过用例

6.1 无条件跳过

6.2 规定条件跳过

6.3 运行


一、简介

1.1 pytest单元测试框架

1、什么是单元测试框架:

单元测试是指在软件开发当中,针对软件的最小单位(函数,方法)进行正确性的检查测试

2、单元测试框架:

        java:junit和testing

        python:unittest和pytest

3、单元测试框架主要做什么:

1.测试发现:从多个文件里面去找到我们测试用例

2.测试执行:按照一定的顺序和规则去执行,并生成结果

3.测试判断:通过断言判断预期结果和实际结果的差异

4.测试报告:统计测试进度、耗时、通过率,生成测试报告

1.2 单元测试框架和自动化测试框架有什么关系

1、什么是自动化测试框架

自动化测试组长针对一个项目开发的一个代码框架,这个框架封装了很多的基础模块,报告模块等等。

2、作用

1.提供测试效率,降低维护成本

2.减少人工干预,提高测试的准确性,增加代码的重用性

3.核心思想是让不懂代码的人也能够通过这个框架去实现自动化测试

3、pytest单元测试框架和自动化测试框架的关系

单元测试框架:只是自动化测试框架中的组成部分之一

pom设计模式:只是自动化测试框架中的组成部分之一

数据驱动.....

关键字驱动

全局配置文件的封装

日志监控

selenium、requests二次封装

断言

报告邮件

更多........

1.3 pytest简介

1.pytest是一非常成熟的python单元框架,比unittest更灵活,容易上手

2.pytest可以和selenium、requests、appium结合实现web自动化,接口自动化,app自动化

3.pytest可以实现测试用例的跳过以及reruns失败用例重试

4.pytest可以和allure生成非常美观的测试报告

5.pytest可以和jenkins持续集成

6.pytest有很多非常强大的插件,并且这些插件能够实现很多的实用的操作

pytest

pytest-xdist 测试用例分布式执行,多cpu并发

pytest-ordering 用于改变测试用例的执行顺序

pytest-rerunfailures 用于失败后重跑

pytest-html (生成html格式的自动化测试报告)

allure-pytest 用于生成美观的测试报告

1.4 pytest安装

1.4.1 pycharm中插件安装:

在文件中新建一个requirements.txt文件,在Terminal中输入命令pip install -r requirements.txt,会在当前文件夹下安装该插件(非全局,仅当前文件夹可用,若想全局环境使用,则在dos(cmd)窗口安装)

文件内容:

pytest

pytest-xdist

pytest-ordering

pytest-rerunfailures

pytest-html

allure-pytest

1.4.2 dos(cmd)窗口安装:

pip install pytest

pip install pytest-xdist

pip install pytest-ordering

pip install pytest-rerunfailures

pip install pytest-html pip install allure-pytest

验证是否安装成功:pytest --version

1.5 使用pytest,默认的测试用例的规则

1.模块名必须以test_开头或以_test结尾 (.py文件)

2.测试类必须以Test开头,并且不能有__init__方法(class 定义的类名)

3.测试方法必须以test开头 (def 定义的函数名)

4.默认从当前目录下收集测试用例,当配置testpath时,从配置的路径下手机测试用例

注:修改配置文件后,模块名、测试类、测试方法应该与配置文件一致

二、基本使用

2.1 创建测试用例

2.1.1简单的函数

def test_01():
    print('sede')

2.1.2 编写测试用例类

class TestHaha():
    def test_01(self):
        assert 1==1
​
    def test_02(self):
        assert 1==2

2.2 执行测试用例

pytest有多种方式来执行用例(unittest框架代码也可通过pytest运行)

2.2.1 通过pytest命令执行测试用例

1.进入要运行的目录下

pytest框架_第1张图片

2.指定运行模块:pytest -vs test_login.py test_product.py         #test_login.py和test_product.py为指定模块名

3.指定运行目录:pytest -vs interface         #interface为指定目录

4.指定运行用例:pytest -vs interface/test_interface.py::test_04_func         #interface为指定目录,test_interface.py为指定模块,test_04_func为指定方法

2.2.2 通过Python代码执行(主函数)

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','test_login.py','test_product.py']) #test_login.py,test_product.py为指定运行模块名

pytest框架_第2张图片

2.2.3 参数详解

-s:表示输出调试信息,包括print打印的信息

import pytest
if __name__ == '__main__':
    pytest.main(['-s']) #主函数模式
​
pytest -s #命令行模式

-v:显示更详细的信息

import pytest
if __name__ == '__main__':
    pytest.main(['-v']) #主函数模式
​
pytest -v #命令行模式

-vs:v和s两个参数一起用

import pytest
if __name__ == '__main__':
    pytest.main(['-vs']) #主函数模式
​
pytest -vs #命令行模式

-n:支持多线程或者分布式运行测试用例

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','-n=2']) #主函数模式
​
pytest -vs -n 2  #命令行模式

--reruns NUM:失败用例重跑,NUM为重跑次数

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','--reruns=2']) #主函数模式
​
pytest --reruns 2 #命令行模式

-x:表示只要有一个用例报错,那么测试停止

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','-x']) #主函数模式
​
pytest -x #命令行模式

--mainfaill=2 :出现两个用例失败就停止

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','--maxfail=2'])#主函数模式
​
pytest --maxfail 2 #命令行模式

-k:根据测试用例的部分字符串指定测试用例

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','-k=baili'])#主函数模式,若函数名包含baili则执行该用例
    
import pytest
if __name__ == '__main__':
    pytest.main(['-vs','-k=not baili'])#主函数模式,若函数名包含baili则执行该用例    
  
​
pytest -k 'baili'#命令行模式
pytest -k 'not baili'#命令行模式,不包含baili则执行

--html ./report/report.html:生成html的测试报告

在项目根目录下新建一个report包用于存储报告

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','--html=./report/report.html'])  #主函数模式
​
#命令行模式
pytest --html ./report/report.html

更多具体参数信息参考官方文档:pytest:帮助你编写更好的程序 — pytest 文档

2.2.4 执行顺序

在模块级别采用模块名的ascii码顺序,在模块内部根据从上往下的定义顺序来执行。

可使用mark标记改变pytest默认的执行顺序

class TestInterface:
    def test_03_interface(self):
        print('test03')
​
    @pytest.mark.run(order=2) #定义运行顺序
    def test_05_interface(self):
        print('test-05')
​
    @pytest.mark.run(order=1) #定义运行顺序
    def test_06_interface(self):
        print('test-06')
​
    def test_07_interface(self):
        print('test-07')
#执行顺序为6-5-3-7

2.3 断言

在pytest中只需使用python语言标准的断言语句 assert 来断言。

2.4 前后置断言

2.4.1 经典的xunit风格

xunit是一种经典且流行的方式,该方式可以基于模块/类/函数实现固定装置(设置和拆卸测试状态)。

1.模块级别的setup_module和teardown_module:

在整个模块调用前后执行一次:

def setup_module():
    '''
    模块级前置条件
    :return:
    '''
    print('我会在当前模块所有测试执行之前执行')
​
def teardown_module():
    '''
    模块级后置条件
    :return:
    '''
    print('我会在当前模块所有测试执行之后执行')
​
​
​
def test_01():
    print('sede')
​
class TestHaha():
    def test_01(self):
        print(234)
​
    def test_02(self):
        print(456)

2.类级别setup_class和teardown_class

在整个类调用前后执行一次:

class TestHaha():
​
    @classmethod
    def setup_class(cls):
        '''
        类级前置条件
        :return:
        '''
        print('我会在当前类所有测试执行之前执行')
​
    @classmethod
    def teardown_class(cls):
        '''
        类级后置条件
        :return:
        '''
        print('我会在当前类所有测试执行之后执行')
​
    def test_01(self):
        print(234)
​
    def test_02(self):
        print(456)

3.方法级别setup_method和teardown_method

在整个方法调用前后执行一次:

class TestHaha():
​
    @classmethod
    def setup_method(self):
        '''
        方法级前置条件
        :return:
        '''
        print('我会在当前类里每个测试方法执行之前执行')
​
    @classmethod
    def teardown_method(self):
        '''
        方法级后置条件
        :return:
        '''
        print('我会在当前类类每个测试方法执行之后执行')
​
    def test_01(self):
        print(234)
​
    def test_02(self):
        print(456)

4.方法级别setup_function和teardown_function

在模块每个函数调用前后执行一次:

def setup_function():
    '''
    函数级别的前置
    :return:
    '''
    print('我会在当前模块在每个测试函数执行之前执行')
​
def teardown_function():
    '''
    函数级别的后置
    :return:
    '''
    print('我会在当前模块在每个测试函数执行之后执行')
​
​
​
​
def test_01():
    print('sede')
​
​
def test_02():
    print('sdjf')

2.4.2 unittest风格

pytest支持unittest的夹具风格(可通过pytest的执行方式执行)

import unittest
​
class TestMay(unittest.TestCase):
​
    @classmethod
    def setUpClass(cls) -> None:
        '''
        类前置条件
        :return:
        '''
        print('我会在当前类所有测试执行之前执行')
​
    @classmethod
    def tearDownClass(cls) -> None:
        '''
        类后置条件
        :return:
        '''
        print('我会在当前类所有测试执行之后执行')
​
    def setUp(self) -> None:
        '''
        方法前置条件
        :return:
        '''
        print('会在当前类里所有方法测试执行之前执行')
​
    def tearDown(self) -> None:
        '''
        方法后置条件
        :return:
        '''
        print('会在当前类里所有方法测试执行之后执行')
​
    def test_one(self):
        print('test')
        self.assertEqual(1,1)
​
    def test_two(self):
        print('cde')

2.4.3 @pytest.fixture

pytest框架有一种通过装饰器实现的夹具机制

1.定义:

通过@pytest.fixture可以定义夹具

import pytest
​
@pytest.fixture
def fixture_func():   # 不能随便接受参数
    print('pytest.fixture的一个前置条件')
    yield  '托尔斯泰'  #yield所在行为返回值,yield上面的代码都是前置,后面的代码都是后置
    print('pytest.fixture的一个后置条件')

2.调用夹具:

1.通过装饰@pytest.mark.usefixtures('fixture_func')

# 使用装饰器语法调用夹具
​
# 修饰函数
@pytest.mark.usefixtures('fixture_func')
def test_function():
    print('我是一个测试函数')
    
​
# 修饰整个类
@pytest.mark.usefixtures('fixture_func')
class TestSome():
​
    def test_one(self):
        print('one')
​
    def test_two(self):
        print('two')

2.通过在测试函数中定义与夹具函数名同名的参数

# 使用装饰器语法调用夹具
@pytest.mark.usefixtures('fixture_func')
def test_function():
    print('我是一个测试函数')

这种方式还可以接收夹具的返回值(夹具的返回值定义在yield所在行)

import pytest
​
@pytest.fixture
def fixture_func():   # 不能随便接受参数
    print('pytest.fixture的一个前置条件')
    yield   'test' #test为夹具的返回值
    print('pytest.fixture的一个后置条件')
​
​
# 使用函数传参的方法来调用前置后置
def test_two(fixture_func):
    print(fixture_func) #通过函数名调用夹具的返回值
    print(12345)
​
# 在类中使用夹具返回值
class TestSome():
​
    def test_one(self,fixture_func):
        print('one')
​
    def test_two(self):
        print('two')
@pytest.fixture
def db():
    import pymysql
    with pymysql.connnect() as conn:
        yield conn   #conn为yield返回值
        
        
@pytest.mark.usefixtures("fixture_func")
def testa(db):
    cursor = db.cursor()   #通过函数名调用夹具的返回值
    cursor.execute('select * from student')
    assert cursor.fetch_one()

3.夹具的作用范围

通过@pytest.fixture装饰器的参数scope可以指定夹具的作用范围

function 默认范围,函数范围,在测试完成后结束

class 在类中最后一共测试完成后结束

module 在模块中最后一个测试完成后结束

package 在包中的最后一个测试完成后结束

session 在一次会话中的最后一共测试完成后结束

修饰类:

import pytest
​
@pytest.mark.usefixtures('class_fixture')
class TestSome():
​
    def test_one(self):
        print('one')
​
    def test_two(self):
        print('two')
​
# 如果使用scope='class'修饰类,在整个类执行前开始,在整个类所有测试执行完之后结束        
@pytest.fixture(scope='class')
def class_fixture():
    print('我是一个类级前置')
    yield
    print('我是一个类级后置')
    
   
# 其他scope参数值作用范围同理

修饰模块:

# 模块级前后置,传入参数autouse=True运行时自动调用
​
@pytest.fixture(scope='module',autouse=True)
def moudle_fixture():
    print('我是一个模块级前置')
    yield
    print('我是一个模块级后置')

修饰包:

在测试用例包下新建conftest.py,内容如下:

import pytest
​
# 设置autouse=True,当pytest执行整个测试用例包内容时,自动执行
@pytest.fixture(scope='package',autouse=True)
def package_fixture():
    print('我在整个包测试开始之前执行')
    yield
    print('我在整个包测试执行完成之后执行')

修饰会话:

在测试用例包下新建conftest.py,内容如下:

import pytest
​
# 设置autouse=True,当pytest执行整个测试用例包内容时,自动执行
@pytest.fixture(scope='session',autouse=True)
def package_fixture():
    print('我在整个测试执行前执行')
    yield
    print('我在整个测试执行后执行')

4.共享夹具

如果一个夹具需要被多个文件使用,则将其定义在conftest.py文件中,通过函数名调用,不需要在测试中倒入,pytest框架会自动发现并执行。

在同一目录下,新增conftest.py和test_01.py文件(在执行时先在当前目录下查找conftest.py中内容,若不存在则查找上层目录、上上层中conftest.py,若不存在,则报错)。

conftest.py文件内容如下:

import pytest
​
@pytest.fixture(scope='class')
def class_fixture():
    print('我是一个类级前置')
    yield
    print('我是一个类级后置')

test_01.py文件内容如下:

import pytest
​
@pytest.mark.usefixtures('class_fixture')
class TestSome():
​
    def test_one(self):
        print('one')
​
    def test_two(self):
        print('two')

pytest运行test_01.py会自动运行执行引用的夹具

5.夹具的多层使用

import pytest
​
@pytest.fixture
def fixture1():
    print('>1111111')
    yield
    print('1111111<')
​
@pytest.fixture
def fixture2():
    print('>222222')
    yield
    print('222222<')
​
@pytest.fixture(scope='module')
def module_fixture():
    print('模块前置')
    yield
    print('模块后置')
​
# 如果有多重夹具,先执行作用范围大的
# 当作用范围相同时,执行先修饰的(参数的传入顺序)
def test_func(fixture1,fixture2,module_fixture):
    print('test_func')

6.夹具的继承

除了可以在测试函数中使用夹具外,夹具功能还可以使用其他的夹具。这有助于夹具的模块化设计,并允许在许多项目中重复使用特定的夹具。

注意夹具的使用只能是使用范围更广的夹具,反过来不行。模块级别的夹具不能使用类级别的夹具。

import pytest
@pytest.fixture
def fixture1():
    print('》11111')
    yield 1
    print('11111《')
​
# 夹具在继承的时候,被继承的夹具范围要大于等于继承夹具
@pytest.fixture
def fixture2(fixture1):   # 需要fixture1的结果
    fixture1 += 1
    print('》22222')
    yield fixture1
    print('22222《')
​
def test_func(fixture2):
    print(fixture2)
    print('test_func')

pytest框架_第3张图片 pytest框架_第4张图片

2.5 参数化

2.5.1 写法一

import pytest
​
@pytest.fixture(params=['avs','vsd','cd3'])
def haha(request):
    print('这是前置的方法')
    yield request.param   #return和yield都表示返回的意思,但是return的后面不能有代码,yield返回后后面可以接代码
    print('这是后置的方法')
​
class TestInterface:
​
    def test_03_interface(self):
        print('test03')
​
    def test_05_interface(self,haha):    #调用了haha方法,会将haha中传入的每个参数执行一次,一共执行三次
        print('test-05')
        print(str(haha))

参数传递过程:在@pytest.fixture(params=['avs','vsd','cd3'])中进行定义,通过request传入haha,request.param获取单个参数值,参数值通过调用函数时haha传入,此时haha=单个函数值

params=['avs','vsd','cd3']这里params是参数名,有s

request.param这里是属性名,是没有s的

注:return和yield都表示返回的意思,但是return的后面不能有代码,yield返回后后面可以接代码

2.5.2 写法二

import pytest
​
test_data=[
    {
        'num':1,
        'expect_data':1
    },
    {
        'num':-1,
        'expect_data':1
    },
    {
        'num':0,
        'expect_data':0
    }
]
​
# 将test_data解包为多个case
@pytest.mark.parametrize('case',test_data)
def test_abs(case):
    assert abs(case['num'])==case['expect_data']
import pytest
​
test_data2=[
    [1,1],
    [-1,1],
    [0,0]
]
​
# 将test_data2解包为多个数据,然后再次解包并赋值
@pytest.mark.parametrize('num,expect',test_data2)
def test_abs2(num,expect):
    assert abs(num)==expect

2.6 夹具别名

name:给表示的是被@pytest.fixture标记的方法取一个别名

import pytest
​
@pytest.fixture(name='aaa')
def haha():
    print('这是前置的方法')
    yield
    print('这是后置的方法')
​
class TestInterface:
​
    def test_03_interface(self):
        print('test03')
​
    def test_05_interface(self,aaa):  #通过别名调用
        print('test-05')   

取了别名之后,函数原本的名称不可用

三、生成报告

3.1 生成测试报告

pytest生成测试报告需要安装插件,请按插件说明来操作官方文档

3.2 生成allure报告

allure是一个专门生成测试报告的框架,支持多种语言和测试框架。官方文档

3.2.1 下载、解压、配置path路径

allure下载地址

解压后运行bin目录下allure.bat文件

配置path路径:【计算机--属性--高级系统设置--环境变量--系统变量--path--编辑】(E:\test ruanjian\allure-commandline-2.14.0\allure-2.14.0\bin)

验证:打开dos窗口,输入命令allure --version

问题:dos可以验证但是pycharm验证失败,则重启pycharm

3.2.2 加入命令生产json格式的临时报告

在pytest命令中带上参数 --alluredir ,指定报告生成的路径

--alluredir ./temp

import pytest
if __name__ == '__main__':
    pytest.main(['-vs','--alluredir=./temp/my_allure_results'])   #主函数模式
    
pytest --alluredir ./temp/my_allure_results   #命令行模式
#也可在配置文件中进行修改

3.2.3 生成allure报告

查看报告需要通过命令行启动allure服务

allure serve ./tmp/my_allure_results

os.system('allure generate ./temp/my_allure_results -o ./report --clean')

allure generate #命令,固定的

./temp #临时的json格式报告的路径

-o #输出output

./report #生成的allure报告的路径

--clean #清空./report路径原来的报告

四、pytest.ini配置文件

pytest.ini这个文件是pytest单元测试框架的核心配置文件(配置文件中,因编码格式问题,需删除注释)

4.1存放位置

一般放在项目的根目录

pytest框架_第5张图片

4.2 编码格式

必须是ANSI,可以使用notpad++修改编码格式,或者新建txt文本,后另存文件为ANSI编码,修改文件名称

pytest框架_第6张图片

4.3 作用

改变pytest默认的行为/规则

4.4 规则明细

不管是主函数的模式运行,命令行模式运行,都会去读取这个文件

[pytest]
addopts=-vs                 #命令行的参数,可以输入多个,用空格分隔,主函数和命令行模式处直接执行
testpaths=./testcase        #测试用例的路径
python_files=test_*.py      #配置测试搜索模块名的规则
python_classes=Test         #配置测试搜索类名的规则
python_functions=test       #配置测试搜索方法名的规则(函数)
markers=                    #标记的模块
    smoke:冒烟用例
    uermanage:用户管理模块
    productmanage:商品管理模块

五、分组执行(冒烟、分模块执行,分接口和web执行)

1.在pytest.ini文件中配置标记的模块

pytest框架_第7张图片

 2.在用例处插入标记

pytest框架_第8张图片

3.通过主函数模式或命令行模式调用mark实现分组执行

#主函数模式
import pytest
if __name__ == '__main__':
    pytest.main(['-vs','-m=smoke'])
    
import pytest
if __name__ == '__main__':
    pytest.main(['-vs','-m=smoke or product'])
    
#命令行模式
pytest -m 'smoke'
pytest -m 'smoke or product'

六、pytets跳过用例

6.1 无条件跳过

无条件跳过,定义用例

@pytest.mark.skip(reason='跳过原因,可不输入')

6.2 规定条件跳过

有条件跳过,定义用例

@pytest.mark.skipif(age>18,reason='跳过原因') #age>18为筛选条件,满足则跳过

6.3 运行

运行:

import pytest
if __name__ == '__main__':
    pytest.main()  #主函数
​
pytest #命令行

你可能感兴趣的:(单元测试,python,pytest)