Fork me on GitHub

移动端测试——Pytest (7)

Pytest的学习

  • 当前使用python3.x版本
  • pytest是python的一种单元测试框架,同自带的Unittest测试框架类似,相比于Unittest框架使用起来更简洁,效率更高
  • 特点:
    ➢ 非常容易上手,入门简单,文档丰富,文档中有很多实例可以参考
    ➢ 支持简单的单元测试和复杂的功能测试
    ➢ 支持参数化
    ➢ 执行测试过程中可以将某些测试跳过或对某些预期失败的Case标记成失败
    ➢ 支持重复执行失败的Case
    ➢ 支持运行由Nose , Unittest编写的测试Case
    ➢ 具有很多第三方插件,并且可以自定义扩展
    ➢ 方便的和持续集成工具集成
import pytest


class Test_pytest:
    # 第一种传参:return返回值
    @pytest.fixture()
    def data(self):
        return 1

    def test_a(self, data):
        assert data == 1

    @pytest.mark.usefixtures("data")  # 拿不到参数
    def test_a_1(self):
        assert False
    # 第二种传参:params参数值
    @pytest.fixture(params=[1, 2, 3])
    def more_data(self, request):
        return request.param

    def test_b(self, more_data):
        assert more_data != 2

    # 第三种传参:parametrize
    @pytest.mark.parametrize("a,b", [(1, 2), (0, 3)])
    def test_c(self, a, b):
        assert a+b == 1

1.1 安装Pytest

1.1.1 安装包方式

  • 进入下载解压后的安装包的路径中
    sudo python setup install

    python setup install

注意:

  • 以管理员身份运行该命令
  • 查看版本
    pytest --version

1.1.2 命令行方式

sudo pip3 install -U pytest

pip3 install -U pytest

注意:

  • 以管理员身份运行该命令
  • 查看版本
    pytest --version

1.1.3 Pytest运行方式

1. 测试类主函数模式

pytest.main()

2. 命令行模式

pytest -s xxx/xxx.py

1.1.4 示例代码测试

vim test_abc.py

import pytest  # 引入pytest包


def test_a():  # test开头的测试函数
    print("------->test_a")
    assert 1  # 断言成功
    
    
def test_b():
    print("------->test_b")
    assert 0  # 断言失败
    
    
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")  # 调用pytest的main函数执行测试

执行结果:

test_abc.py 
------->test_a
. # .(代表成功)
------->test_b
F # F(代表失败)

1.2 Pytest使用

1.2.1 Pytest的setup和teardown函数

  • setup和teardown主要分为:模块级、类级、功能级、函数级
  • 存在于测试类内部

1. 函数级别

setup()teardown()

  • 在一个类的内部
  • 运行于测试方法的始末,即:运行一次测试函数会运行一次setup和teardown
import pytest


class Test_ABC:
    # 函数级开始
    def setup(self):
        print("------->setup_method")
    # 函数级结束

    def teardown(self):
        print("------->teardown_method")

    def test_a(self):
        print("------->test_a")
        assert 1

    def test_b(self):
        print("------->test_b")


if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py 
------->setup_method # 第一次 setup()
------->test_a
.
------->teardown_method # 第一次 teardown()
------->setup_method # 第二次 setup()
------->test_b
.
------->teardown_method # 第二次 teardown()

2. 类级别

setup_classteardown_class

  • 在一个类的内部
  • 运行于测试类的始末,即:在一个测试内只运行一次,不关心测试类内有多少个测试函数
import pytest
 
          
class Test_ABC:
    # 测试类级开始
    def setup_class(self):
        print("------->setup_class")
        
    # 测试类级结束
    def teardown_class(self):
        print("------->teardown_class")
        
    def test_a(self):
        print("------->test_a")
        assert 1
        
    def test_b(self):
        print("------->test_b")
        
        
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py 
------->setup_class # 第一次 setup_class()
------->test_a
.
------->test_b
F 
------->teardown_class # 第一次 teardown_class()

1.2.2 Pytest配置文件

pytest的配置文件通常放在测试目录下,名称为pytest.ini,命令行运行时会使用该配置文件中的配置

GBK编码

  • 配置pytest命令行运行参数

空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数

[pytest]
addopts = -s ... 
  • 配置测试搜索的路径

当前目录下的scripts文件夹 -可自定义

[pytest]
testpaths = ./scripts  
  • 配置测试搜索的文件名

当前目录下的scripts文件夹下,以test_开头,以.py结尾的所有文件 -可自定义

[pytest] 
python_files = test_*.py     
  • 配置测试搜索的测试类名

当前目录下的scripts文件夹下,以test_开头,以.py结尾的所有文件中,以Test_开头的类 -可自定义

[pytest]
python_classes = Test_*  
  • 配置测试搜索的测试函数名

当前目录下的scripts文件夹下,以test_开头,以.py结尾的所有文件中,以Test_开头的类内,以test_开头的方法 -可自定义

[pytest]
python_functions = test_*  
  • default
[pytest]
# 命令行参数
addopts = -s --reruns 1 --html=./report/test_report.html
# 测试路径
testpaths = ./scripts
# 搜索文件名
python_files = test001.py
# 搜索测试类名
python_classes = Test_*
# 搜索测试方法名
python_functions = test_*

1.2.3 Pytest常用插件

文件路径:
Test_App/{test_abc.py,pytest.ini}

pyetst.ini配置文件内容:
有中文就用GBK编码

[pytest]
# 命令行参数
addopts = -s
# 搜索文件名
python_files = test_*.py
 # 搜索的类名
python_classes = Test_*
# 搜索的函数名
python_functions = test_*

1. Pytest测试报告

通过命令行方式,生成xml/html格式的测试报告,存储于用户指定路径
插件名称:pytest-html

  • 安装方式:
  1. 安装包方式
    python setup.py install
  2. 命令行
    pip3 install pytest-html

    pip3 install -U pytest-html
  • 使用方法:
    格式:pytest --html=用户路径/report.html
import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
        
    def teardown_class(self):
        print("------->teardown_class")
        
    def test_a(self):
        print("------->test_a")
        assert 1
        
    def test_b(self):
        print("------->test_b")
        assert 0  # 断言失败

提示:运行方式

  1. 修改Test_App/pytest.ini文件,添加报告参数
    addopts = -s --html=./report.html
参数 说明
-s 输出程序运行信息
--html=./report.html 在当前目录下生成report.html文件

若要生成xml文件,可将--html=./report.html改成--html=./report.xml
2. 命令行运行
cd Test_App
pytest
3. 执行结果
在当前目录会生成assets文件夹和report.html文件

2. Pytest控制函数执行顺序

函数修饰符的方式标记被测试函数执行的顺序
插件名称:pytest-ordering

  • 安装方式:
  1. 安装包方式
    python setup.py install
  2. 命令行
    pip3 install pytest-ordering

    pip3 install -U pytest-ordering
  • 使用方法:
  1. 标记于被测试函数
    格式:@pytest.mark.run(order=x)
  2. 根据order传入的参数来解决运行顺序
  3. order值全为正数或全为负数时,运行顺序:值越小,优先级越高
  4. 正数和负数同时存在:正数优先级高
  5. order值为负数时,优先级低于没有被标记的测试方法
  6. order值为正数时,优先级高于没有被标记的测试方法

默认情况下,pytest是根据测试方法名由小到大执行的,可以通过第三方插件包改变其运行顺序

eg:

第一种:默认执行方式

import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
        assert 0
        
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

结果:

test_abc.py 
------->setup_class
------->test_a # 默认第一个运行
.
------->test_b # 默认第二个运行
F
------->teardown_class

第二种:

import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")

    def teardown_class(self):
        print("------->teardown_class")
    @pytest.mark.run(order=2)
    def test_a(self):
        print("------->test_a")
        assert 1

    @pytest.mark.run(order=1)
    def test_b(self):
        print("------->test_b")
        assert 0

        
if __name__ == '__main__':
        pytest.main("-s  test_abc.py")

结果:

test_abc.py
------->setup_class
------->test_b # order=1 优先运行
F
------->test_a # order=2 晚于 order=1 运行
.
------->teardown_class

3. Pytest失败重试

通过命令行方式,控制失败函数的重试次数
插件名称:pytest-rerunfailures

  • 安装方式:
    1.安装包方式
    python setup.py install
    2.命令行
    pip3 install pytest-rerunfailures

    pip3 install -U pytest-rerunfailures
  • 使用方法:
    命令行格式:n为重试的次数
    pytest --reruns n

eg:

import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
        assert 0 # 断言失败
  • 运行方式:
    1.修改Test_App/pytest.ini文件,添加失败重试参数
    addopts = -s --reruns 2 --html=./report.html
    -s 输出程序运行信息
    --reruns 2 失败测试函数重试两次
    --html=./report.html 在当前目录下生成report.html文件
    2.命令行进入Test_App目录
    3.执行命令:
    pytest
  • 执行结果:
    在测试报告中可以看到两次重试记录

4. pytest之fixture

fixture修饰器来标记固定的工厂函数,在其他函数、模块、类或整个工程调用它时,会被激活并优先执行,通常会被用于完成预置处理重复操作

  • 用在conftest.py文件中,被fixure标记的函数可作为全局变量
  • 方法:
    fixture(scope="function", params=None, autouse=False, ids=None, name=None)
常用参数 说明
scope 被标记方法的作用域
function default,作用于每个测试方法,每个test都运行一次
class 作用于整个类,每个class的所有test只运行一次
module 作用于整个模块,每个module的所有test只运行一次
session 作用于整个session(慎用),每个session只运行一次
params (list类型)提供参数数据,供调用标记方法的函数使用
autouse 是否自动运行,默认为False不运行,设置为True自动运行

eg:

  1. 通过参数引用
    @pytest.fixture()
import pytest


class Test_ABC:
    @pytest.fixture()
    def before(self):
        print("------->before")
    def test_a(self,before): # ⚠️ test_a方法传入了被fixture标识的函数,已变量的形式
        print("------->test_a")
        assert 1
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py 
------->before # 发现before会优先于测试函数运行
------->test_a
.
  1. 通过函数引用
    @pytest.fixture()
    @pytest.mark.usefixtures("before")
import pytest

@pytest.fixture() # fixture标记的函数可以应用于测试类外部
def before():
    print("------->before")
@pytest.mark.usefixtures("before")
class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py 
------->before # 发现before会优先于测试类运行
------->setup
------->test_a
.
  1. 默认设置为运行
    @pytest.fixture(autouse=True) # 设置为默认运行
import pytest


@pytest.fixture(autouse=True) # 设置为默认运行
def before():
    print("------->before")
class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py 
------->before # 发现before自动优先于测试类运行
------->setup
------->test_a
.
  1. 设置作用域为function
    @pytest.fixture(scope='function',autouse=True) # 作用域设置为function,自动运行
import pytest


@pytest.fixture(scope='function',autouse=True) # 作用域设置为function,自动运行
def before():
    print("------->before")
class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
        assert 1
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py
------->before # 运行第一次
------->setup
------->test_a
.------->before # 运行第二次
------->setup
------->test_b
.
  1. 设置作用域为class
    @pytest.fixture(scope='class',autouse=True) # 作用域设置为class,自动运行
import pytest


@pytest.fixture(scope='class',autouse=True) # 作用域设置为class,自动运行
def before():
    print("------->before")
class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
        assert 1
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

执行结果:

test_abc.py
------->before # 发现只运行一次
------->setup
------->test_a
.
------->setup
------->test_b
.
  1. 返回值

第一种:@pytest.fixture()

import pytest


@pytest.fixture()
def need_data():
    return 2 # 返回数字2

class Test_ABC:
    def test_a(self,need_data):
        print("------->test_a")
        assert need_data != 3 # 拿到返回值做一次断言

if __name__ == '__main__':
pytest.main("-s  test_abc.py")

执行结果:

test_abc.py 
------->test_a
.

第二种:
@pytest.fixture(params=[1, 2, 3])
request
request.param

  • 运行三次
import pytest


@pytest.fixture(params=[1, 2, 3])
def need_data(request): # 传入参数request系统封装参数
    return request.param # 取列表中单个值,默认的取值方式

class Test_ABC:
    def test_a(self,need_data):
        print("------->test_a")
        assert need_data != 3 # 断言need_data不等于3

if __name__ == '__main__':
pytest.main("-s  test_abc.py")

执行结果:

# 可以发现结果运行了三次
test_abc.py 
1
------->test_a
.
2
------->test_a
.
3
------->test_a
F

5. 跳过测试函数

根据特定的条件,不执行标识的测试函数

  • 格式:skipif(condition, reason=None)
参数 说明
condition 跳过的条件,必传参数
reason 标注原因,必传参数
  • 使用方法:
    @pytest.mark.skipif(condition, reason="xxx")

eg:

import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")
    def test_a(self):
        print("------->test_a")
        assert 1
    @pytest.mark.skipif(condition=2>1,reason = "跳过该函数") # 跳过测试函数test_b
    def test_b(self):
        print("------->test_b")
        assert 0

执行结果:

test_abc.py 
------->setup_class
------->test_a #只执行了函数test_a
.
------->teardown_class
s # 跳过函数

6. 标记为预期失败函数

标记测试函数为失败函数

  • 格式:xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用参数 名称
condition 预期失败的条件,必传参数
reason 失败的原因,必传参数
  • 使用方法:
    @pytest.mark.xfail(condition, reason="xx")

eg:

import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")
    def test_a(self):
        print("------->test_a")
        assert 1
    @pytest.mark.xfail(2 > 1, reason="标注为预期失败") # 标记为预期失败函数test_b
    def test_b(self):
        print("------->test_b")
        assert 0

执行结果:

test_abc.py 
------->setup_class
------->test_a
.
------->test_b
------->teardown_class
x  # 失败标记

7. 函数数据参数化

方便测试函数对测试属于的获取

  • 格式:parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
常用参数 说明
argnames 参数名
argvalues 参数对应值,类型必须为list

当参数是一个值时,格式为
[value]
当参数个数大于一个时,格式为
[(param_value1,param_value2.....),(param_value1,param_value2.....)]

  • 使用方法:
    @pytest.mark.parametrize(argnames,argvalues)
    ⚠️ 参数值为N个,测试方法就会运行N次

eg:

  1. 单个参数示例
import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")

    @pytest.mark.parametrize("a",[3,6]) # a参数被赋予两个值,函数会运行两遍
    def test_a(self,a): # 参数必须和parametrize里面的参数一致
        print("test data:a=%d"%a)
        assert a%3 == 0

执行结果:

test_abc.py 
------->setup_class
test data:a=3 # 运行第一次取值a=3
.
test data:a=6 # 运行第二次取值a=6
. 
------->teardown_class
  1. 多个参数示例
import pytest


class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")

    @pytest.mark.parametrize("a,b",[(1,2),(0,3)]) # 参数a,b均被赋予两个值,函数会运行两遍
    def test_a(self,a,b): # 参数必须和parametrize里面的参数一致
        print("test data:a=%d,b=%d"%(a,b))
        assert a+b == 3

执行结果:

test_abc.py 
------->setup_class
test data:a=1,b=2 # 运行第一次取值 a=1,b=2
.
test data:a=0,b=3 # 运行第二次取值 a=0,b=3
.
------->teardown_class
  1. 函数返回值类型示例
import pytest


def return_test_data():
    return [(1,2),(0,3)]
    
class Test_ABC:
    def setup_class(self):
        print("------->setup_class")
    def teardown_class(self):
        print("------->teardown_class")

    @pytest.mark.parametrize("a,b",return_test_data()) # 使用函数返回值的形式传入参数值
    def test_a(self,a,b):
        print("test data:a=%d,b=%d"%(a,b))
        assert a+b == 3

执行结果:

test_abc.py 
------->setup_class
test data:a=1,b=2 # 运行第一次取值 a=1,b=2
.
test data:a=0,b=3 # 运行第二次取值 a=0,b=3
.
------->teardown_class

更新中......


posted @ 2019-09-24 22:47  紫枫一梦千年  阅读(134)  评论(0编辑  收藏  举报