pytest

pytest 是一个成熟、全套的 python 自动化测试工具,旨在帮助你写出更好的程序。它可以用来做单元测试,也可以用来做功能测试、接口自动化测试;相比 unittest,它能支持更多、更全面的功能,有着以下特色和优势。

直接使用纯粹的 python 语言, 不需要你过多学习框架特定的语法,例如 self.assert* 等,以此减少学习成本;

  • pytest 框架不需要写诸如 setUp()、tearDown() 这样的方法,它可以直接开始测试;
  • pytest 可以自动识别测试用例,无须像 unittest 一样将测试用例放进 TestSuite 里组装;
  • test fixtures 包括数据参数化测试非常好用;
  • pytest 支持错误重试;
  • pytest 支持并发测试。

pytest安装

pytest 不是 python 标准库,故使用时需要安装:

1
2
3
4
# 安装
pip install -U pytest
# 查看
pytest --version

pytest使用

pytest简单使用

如下Python脚本:

1
2
3
4
5
6
7
8
class TestSample(object):
# 测试用例默认以test开头
def test_equal(self):
assert 1 == 1

def test_not_equal(self):
assert 1 != 0

在这个测试文件(test.py)里,我定义了一个测试类 TestSample,然后在这个测试类下面定义了两个测试用例,分别是 test_equal 和 test_not_equal,下面来运行下这个测试类:

image-20201217143900538

pytest 直接运行 unittest 测试用例

pytest 兼容 unittest,原来使用 unittest 框架写的代码,可以被 pytest 直接调用。

在创建tests/test_sample.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import unittest


# 测试类必须要继承TestCase类
class TestSample(unittest.TestCase):
# 测试用例默认以test开头
def test_equal(self):
self.assertEqual(1, 1)

def test_not_equal(self):
self.assertNotEqual(1, 0)


if __name__ == '__main__':
unittest.main()

在运行时,我们可以直接用如下方式调用:

image-20201217144413177

如果你想运行整个测试用例集,或者你想把之前用 unittest 的测试用例全部换成用 pytest 执行,该如何操作呢?

我们把 main.py 文件里,所有关于执行测试用例的部分,从 unittest 执行更改为 pytest 执行,更改后的 main.py 函数如下

image-20201217145120710

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import glob
import os

import pytest


# 查找所有待执行的测试用例module,见《04|必知必会,打好Python基本功》
def find_modules_from_folder(folder):
absolute_f = os.path.abspath(folder)
md = glob.glob(os.path.join(absolute_f, "*.py"))
return [f for f in md if os.path.isfile(f) and not f.endswith('__init__.py')]


if __name__ == "__main__":
# 得出测试文件夹地址
test_folder = os.path.join(os.path.dirname(__file__), 'tests')
# 得出测试文件夹下的所有测试用例
target_file = find_modules_from_folder(test_folder)
# 直接运行所有的测试用例
pytest.main([*target_file, '-v'])

然后在命令行下执行:

image-20201217145222814

正因为 pytest 完全兼容 unittest,以及具备刚刚介绍的那些诸多优点,才使得 pytest 风靡于整个 python 社区。

pytest 查找测试用例的原则

指定命令行参数时的查找原则

如果指定了命令行参数,则根据命令行参数执行。

这句话很好理解,像我们之前的测试里,均指定了测试要执行的 module(例如 “python -m pytest tests/test_sample.py”),故 pytest 只会查找 test_sample.py 文件。

未指定命令行参数时的查找原则

如果未指定命令行参数(即直接在命令行输入 pytest),则从 testpath(已配置)或从当前目录开始查找可用的测试用例, 其步骤如下:

  • 搜索由任何符合以下规则的文件 test_*.py*_test.py文件。
  • 找到后,从这些文件中,收集如下测试项:test 为前缀的函数;Test 为前缀的类里面的以 test 为前缀的函数。

举个例子来理解下这个原则,看如下的工程结构:

image-20201217145801022

注意:我有一个测试文件为 sample.py,里面包括两个测试用例,然后我 tests 文件夹下有两个 .py 文件共计 4 个测试用例,如果我在命令行里输入以下命令执行及结果:

image-20201217145853050

仅仅有 4 个测试用例运行了,但不包括 sample.py 里的两个测试用例,这就是 pytest 默认查找在起作用,因为运行 pytest 时,我没有指定运行某个文件,所以 pytest 自动在当前目录下查找以“test_”开头或者以“_test” 结尾的 py 文件,显然sample.py 不符合这个规则,故被忽略了。

如果直接在命令行运行sample.py:

image-20201217150008253

执行看到 sample.py 里的两个测试方法都被执行了(因为指定了运行文件)。

接下来,更改 sample.py 为 test_sample.py,然后把这个文件里的方法“test_equal”改成“equal_test”,于是项目文件结构如下:

image-20201217150243559

在命令运行及结果:

image-20201217150405103

共有 5 个测试被执行,其中不包括 equal_test 方法,因为它不是以 test 开头。

pytest运行方式详解及其参数

pytest 有两个测试运行方式:

  • 命令行运行
  • pytest.main() 运行

命令行运行

pytest 支持在命令行中以如下方式运行:

1
 python -m pytest [...]

pytest.main() 运行

除了命令行运行方式外,pytest 还支持在程序中运行,在程序中运行的命令如下:

1
pytest.main([...])

不管是使用命令行运行或者使用 pytest.main() 的方式运行,它们支持的参数都是一样的。需要注意的是:pytest 的参数必须放在一个 list 或者 tuple 里。

pytest 参数

pytest 支持特别多的参数,具体有哪些参数可以通过如下命令查看:

1
pytest --help

常用参数:

-m: 用表达式指定多个标记名。

pytest 提供了一个装饰器 @pytest.mark.xxx,用于标记测试并分组(xxx是你定义的分组名),以便你快速选中并运行,各个分组直接用 and、or 来分割。

-v: 运行时输出更详细的用例执行信息

不使用 -v 参数,运行时不会显示运行的具体测试用例名称;使用 -v 参数,会在 console 里打印出具体哪条测试用例被运行。

-q: 类似 unittest 里的 verbosity,用来简化运行输出信息。

使用 -q 运行测试用例,仅仅显示很简单的运行信息, 例如:

1
2
.s..   [100%]
3 passed, 1 skipped in 9.60s

-k: 可以通过表达式运行指定的测试用例。
它是一种模糊匹配,用 and 或 or 区分各个关键字,匹配范围有文件名、类名、函数名。

-x: 出现一条测试用例失败就退出测试。

在调试时,这个功能非常有用。当出现测试失败时,停止运行后续的测试。

运行指定文件夹下的测试用例

pytest 支持更简单的方法实现指定文件夹运行:

1
2
3
4
# 执行所有当前文件夹及子文件夹下的所有测试用例
pytest .
# 执行跟当前文件夹同级的tests文件夹及子文件夹下的所有测试用例
pytest ../tests

除此之外,pytest 还允许你通过更复杂的方式来挑选测试用例执行。例如,我们可以通过 -m 或者 -k 的参数,把我们的测试用例限制在某一个文件夹下,这样就实现了仅允许指定文件夹下的测试用例。

选择测试用例执行

pytest 里选择测试用例执行有很多方法,可以按照测试文件夹、测试文件、测试类和测试方法四种。

  • 按照测试文件夹执行

    • “运行指定文件夹下的测试用例”中已说明
  • 按照测试文件执行

    1
    2
    # 运行test_sample.py下的所有的测试用例
    pytest test_sample.py
  • 按照测试类执行

    按照测试类执行,必须以如下格式:

    pytest 文件名 .py:: 测试类,其中“::”是分隔符,用于分割测试 module 和测试类。

    1
    2
    # 运行test_sample.py文件下的,类名是TestSample下的所有测试用例
    pytest test_sample.py::TestSample
  • 按照测试方法执行

    同样的测试方法执行,必须以如下格式:

    pytest 文件名 .py:: 测试类 :: 测试方法,其中 “::” 是分隔符,用于分割测试 module、测试类,以及测试方法。

    1
    2
    # 运行test_sample.py文件下的,类名是TestSample下的,名字为test_equal的测试用例 
    pytest test_sample.py::TestSample::test_equal
  • 不在命令行执行,在程序中执行

    以上选择测试用例执行的方法,可以不在命令行,而直接在测试程序里执行,其语法为pytest.main([模块.py::类::方法])

动态挑选测试用例运行 — 按 Tag

动态挑选测试用例一直是测试框架的刚需,在 pytest 里动态挑选测试用例需要借助两个步骤。

  • 首先给测试用例打标签(mark),在 Class、method 上加上如下装饰器:

    1
    @pytest.mark.xxx
  • 在运行时,命令行动态指定标签运行:

    1
    2
    3
    4
    5
    6
    # 同时选中带有这两个标签的所有测试用例运行
    pytest -m "mark1 and mark2"
    # 选中带有mark1的测试用例,不运行mark2的测试用例
    pytest -m "mark1 and not mark2"
    # 选中带有mark1或 mark2标签的所有测试用例
    pytest -m "mark1 or mark2"

    下面来实际演示下,我们更改 tests 文件夹下的两个文件,其中对 test_baidu.py 文件的修改如下:

image-20201217152549914

对test_sample.py文件修改如下:

image-20201217152948461

给这两个测试类分别加上了标签 baidu 和标签sample,现在我们按需运行及查看结果:

image-20201217153649825

可以看到baidu下面的两条用例执行了,但是sample下面的两条用例没有执行,在 Console 中显示 “2 deselected”。

注意,查看上面的执行结果有警告,也给定了官方地址说明解决问题,有两种解决方式(二选一):

  • 添加pytest.ini配置文件,指定标签名

    1
    2
    3
    [pytest]
    markers = baidu
    sample

    image-20201217171243588

  • 添加pytest_configure.py配置类(该方式未生效,不明所以)

    1
    2
    3
    4
    5
    # 单个标签
    def pytest_configure(config):
    config.addinivalue_line(
    "markers", "baidu"
    )
    1
    2
    3
    4
    5
    6
    7
    # 多个标签
    def pytest_configure(config):
    marker_list = ["baidu", "sample"]
    for marker in marker_list:
    config.addinivalue_line(
    "markers", marker
    )

动态挑选测试用例运行 — 按名称

pytest 中,动态挑选测试用例,除了打标签(mark)外,还有另外一种方式:

1
2
# -k 参数是按照文件名、类名、方法名来模糊匹配的
pytest -k xxxPattern

下面进行详细的演示,项目结构如下:

1
2
3
4
5
6
7
8
|--test_framework
|--tests
|--test_baidu.py
|--test_sample.py
|--__init__.py
|--common
|--__init__.py
|--__init__.py
  • test_baidu.py 里定义了一个测试类 Baidu, 这个测试类下有两个测试方法 test_baidu_search 和 test_baidu_set;

  • test_sample.py 中定义了一个测试类 TestSample,这个测试类下面有两个测试方法 test_equal和 test_not_equal。

在命令行中以如下方式运行。

  • 按照文件名称全匹配:

    1
    2
    # 运行test_baidu.py下的所有的测试
    pytest -k "test_baidu.py"

image-20201221152343886

  • 按照文件名字部分匹配:

    1
    2
    # 因为baidu能匹配上test_baidu.py,故运行test_baidu.py下所有的测试
    pytest -k "baidu"

    image-20201221152634479

  • 按照类名匹配:

    1
    2
    # 因为Baidu能匹配上test_baidu.py里定义的测试类Baidu,故运行Baidu测试类下所有的测试。 你也可以写成Bai
    pytest -k "Baidu"

    image-20201221152741989

  • 按照方法名匹配:

    1
    2
    # equal能匹配test_sample.py中定义的测试类TestSample下的测试方法test_equal和test_not_equal, 这两个方法都会执行
    pytest -k "equal"

    image-20201221153136057

忽略测试用例执行

有挑选测试用例执行,那么就一定会有忽略测试用例执行,忽略测试用例执行有如下 3 种方式:

  • 直接忽略测试执行

    • 直接忽略可以使用 @pytest.mark.skip 装饰器来实现。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import pytest


    # 在TestSample这个类上加标签sample
    @pytest.mark.sample
    class TestSample(object):
    # 测试用例默认以test开头
    @pytest.mark.skip(reason='skip此测试用例')
    def test_equal(self):
    assert 1 == 1

    def test_not_equal(self):
    assert 1 != 0

    以上示例,在TestSample里定义的测试方法test_equal,然后加上装饰器pytest.mark.skip,那么在执行测试时test_equal会被忽略

    image-20201221153718944

  • 按条件忽略测试执行 — 使用 skipif 忽略

    • 按 skipif 条件,当条件符合时便会忽略某条测试用例执行。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import pytest


    # 在TestSample这个类上加标签sample
    @pytest.mark.sample
    class TestSample(object):
    # 定义一个flag,用来指示是否要skip一个测试用例
    flag = 1

    # 此处判断flag的值,为1则忽略,否则则不忽略
    @pytest.mark.skipif(flag == 1, reason='skip此测试用例')
    def test_equal(self):
    assert 1 == 1

    def test_not_equal(self):
    assert 1 != 0

    执行用例时,test_equal会被忽略

    image-20201221154723328

    更改flag的值为非1时,再次执行用例,该方法将会被执行。

  • 按条件忽略测试执行 — 使用 -m 或者 -k 忽略

    • 除了 skip 和 skipif 外,我们也可以通过 -m 或者 -k 的方式,把我们不需要运行的测试用例给过滤掉,从而实现测试用例的忽略执行。

setUp 和 tearDown 详解

1. 按 module 进行 setup 和 tear down

按 module 进行 setup 和 tear down,即在某一个 module 内 setup 或者 tear down 的方法只会执行一次,pytest 里用于 module 的 set up 和 tear down 方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def setup_module(module):
"""
module级别的setup,直接定义为一个module里的函数
在本module里所有test执行之前,被调用一次
"""
# 代码...
print("------ set up for module ------")


def teardown_module(module):
"""
module级别的setup,直接定义为一个module里的函数
在本module里所有test执行之后,被调用一次
"""
# 代码...
print("------ tead down for module ------")

需注意以下几个事情:

  • setup_module(module) 和 teardown_module(module) 的写法最好不要改动;
  • 当 setup_module 出错,teardown_module 不会被执行;
  • 一个 module(.py 文件)可以包括多个 Class,多个classs 下可能有多个 case,但是 setup_module 和 teardown_module 只会执行一次。

2. 按 class 进行 setup 和 tear down

在某一个测试类内,同样可以进行 set up 和 tear down。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Baidu(object):
@classmethod
def setup_class(cls):
"""
仅在当前测试类下的所有test执行之前,被调用一次
注意它必须以@classmethod装饰
"""
# 代码...
print("------ set up for class------")

@classmethod
def teardown_class(cls):
"""
仅在当前测试类下的所有test执行之后,被调用一次
注意它必须以@classmethod装饰
"""
# 代码...
print("------tear down for class------")

需注意以下几个事情:

  • setup_class(cls) 和 teardown_class(cls) 的写法最好不要改动。

  • setup_class(cls) 和 teardown_class(cls) 必须以 @classmethod 装饰。

  • 当 setup_class(cls) 出错,teardown_class(cls) 不会被执行。

3.按 method 进行 setup 和 tear down

针对每一个测试用例,同样可以进行 set up 和 tear down。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def setup_method(self, method):
"""
在当前测试类里,每一个test执行之前,被调用一次
"""
# 代码...
print("------set up for method------")

def teardown_method(self, method):
"""
在当前测试类里,每一个test执行之前,被调用一次
"""
# 代码...
print("------tear down for method------")

需注意以下两个事情:

  • setup_method(self, method) 和 teardown_method(self, method) 的写法最好不要改动。

  • 当 setup_method(self, method)用例执行失败时,teardown_method(self, method) 不会被执行。

setup 和 teardown 在我们测试开始和结束后准备/清理测试数据,系统状态时非常有用。

4.使用 pytest.ini 文件破除默认 pattern,灵活命名测试文件

pytest 查找测试用例,会根据测试用例名,仅默认查找前缀以 _tes开头或者后缀以 _test 结尾的测试文件;而查找测试方法,仅查找测试类以 Test 开头,测试方法以 test 开头。

那有没有办法破除这一依赖呢?当然有,那就是使用 pytest.ini 文件。

pytest.ini 是 pytest 的主配置文件,可以改变 pytest 的默认行为。在项目根目录 lagoutAPITest 下新创建一个文件 pytest.ini,我们的项目结构就变成这样:

1
2
3
4
5
6
7
8
9
|--test_framework
|--tests
|--test_baidu.py
|--test_sample.py
|--__init__.py
|--common
|--__init__.py
|--__init__.py
|--pytest.ini

pytest.ini 的内容如下:

1
2
3
4
[pytest]
python_classes = *
python_files= *
python_functions = test*
  • python_classes,表示要匹配的测试类的 pattern,*匹配所有。
  • python_files,表示要匹配的测试文件。
  • python_functions,表示要匹配的测试方法。

在这里我把 python_files 和 python_classes 均设置为*,表示任何 *.py 都将被认为是测试文件。任何名字的测试类都将被认为是测试类。

新建一个sample.py的测试文件,内容如下:

1
2
3
4
5
class Sample(object):
def test_equal(self):
assert 1 == 1
def not_equal(self):
assert 1 != 0

定义了一个 Sample 类,其中包括两个测试方法 test_equal 和 not_equal。然后我切换到项目根目录下,在命令行运行及结果:

image-20201222150310495

可以看到,sample.py 这个文件被当作了测试文件,并且 Sample 这个类也被 pytest 视为测试类,因为他们符合 pytest.ini 里的配置。

而 Sample 类的两个测试方法中,test_equal 被执行,而 not_equal 没有被执行,因为我在 pytest.ini 里关于 python_functions 的配置是必须以 test 开头。

使用 pytest.ini 可以针对 pytest 做更多配置,做法如下所示。

  • 注册标签

    1
    2
    3
    4
    [pytest]
    markers =
    baidu
    sample

    可以在 pytest.ini 里注册标签,并统一管理。

    在 pytest.ini 文件里注册标签并不意味着你不需要在测试类/测试方法里忽略 @pytest.mark.xxx 装饰器,你仍需要在你的测试用例上加上标签。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import pytest


    # 在TestSample这个类上加标签sample
    @pytest.mark.sample
    class TestSample(object):
    # 定义一个flag,用来指示是否要skip一个测试用例
    flag = 1

    # 此处判断flag的值,为1则忽略,否则则不忽略
    @pytest.mark.skipif(flag == 2, reason='skip此测试用例')
    def test_equal(self):
    assert 1 == 1

    def test_not_equal(self):
    assert 1 != 0
  • 指定测试目录

    1
    2
    [pytest]
    testpaths=tests

    此方法把 testpaths 指定为 tests,当你在命令行中直接执行 pytest 时,会自动查询 testpaths 文件夹,如果没有设置 testpaths,则会自动从当前文件夹找起。

    除了以上这些,pytest.ini 还有其他很多用处,例如更改默认命令行参数、指定 pytest 最低版本号、指定忽略某些目录、禁用 XPASS 等功能等。

5. 失败重跑测试用例

在测试执行中,偶尔会出现由于环境不稳定,或者网络不稳定造成的测试运行失败的情况,如果第一次运行就报错,那么势必会增加我们排查的工作量。

所以 pytest 支持错误失败重跑,使用失败重跑机制的步骤如下:

  1. 安装

    1
    pip install -U pytest-rerunfailures
  1. 命令行执行失败重跑次数

    1
    2
    # 语法:
    --reruns Num。 其中Num是重跑的次数

    下面我们来实际看一个 re-run 的例子,我更改 sample.py 文件如下:

1
2
3
4
5
6
7
8
class Sample(object):
def test_equal(self):
# 在这里,我让这个case失败,来演示re-run
assert 1 == 0

def not_equal(self):
assert 1 != 0

命令执行及结果:

image-20201222151001460

执行后观察测试输出,会发现输出信息“1 failed,2 rerun”:

6. 并发运行测试用例集

当你的测试用例比较多时,最好可以通过并发测试来减少测试整体的运行时间。pytest 支持并发测试,并且有不同的并发测试库,其中如下两个比较著名:

pytest-parallel

安装

1
pip install pytest-parallel

运行:使用 pytest-parallel 运行,需要指定参数。

1
–workers (optional) X

多进程运行, X 是进程数,默认值 1。

1
–tests-per-worker (optional) X

多线程运行, X 是每个 worker 运行的最大并发线程数, 默认值1。

注意:这个插件仅仅支持 python 3.6 版本及以上,而且如果你想多进程并发,必须跑在 Unix 或者 Mac 机器上,windows 环境仅仅支持多线程运行。

运行命令如下:

1
2
3
pytest --workers 2 #指定2个进程并发
#指定2个进程并发,每个进程最多运行3个线程
pytest --workers 2 --test-per-worker 3
pytest-xdist

安装

1
pip install pytest-xdist

运行

1
2
3
4
5
6
# 语法:
pytest -n NUMCPUS
# 以下为2个进程并行运行
pytest -n 2
#使用与CPU内核一样多的进程来并发
pytest -n auto

从理论上来说,pytest-parallel 要更好一些,因为 pytest-xdist 有以下缺点:

  • 非线程安全
  • 多线程时性能不佳
  • 需要状态隔离

但是实际应用中,pytest-parallel 有时会出现如下运行错误:

1
BrokenPipeError: [WinError 109] 管道已结束

而且这个错误发生的原因不确定,官方暂时没有修复, 如果你在测试中发现这个错误,那么可以使用 pytest-xdist 来进行并发测试。

pytest 集成测试报告

pytest-html

  • 安装

    1
    pip install pytest-html
  • 使用

    1
    pytest --html=report.html
  • 演示

    image-20201222151744292

  • 报告

    • 执行完成后会在项目的根目录下生成一个report.html文件

      image-20201222151851442

    • 浏览器打开report.html文件

      image-20201222151925318

pytest-html 还支持错误重试,使用如下命令运行

1
pytest --html=report.html --self-contained-html --reruns 2

运行结束你会发现,错误的测试用例被运行了 2 次。

image-20201222152142742

allure

  • 安装:在不同操作系统上安装 allure 的步骤是不同的

    • MacOS

      1
      2
      3
      4
      5
        # 安装
      brew install allure

      # 查看安装版本
      allure --version
    • Linux

      1
      2
      3
      sudo apt-add-repository ppa:qameta/allure
      sudo apt-get update
      sudo apt-get install allure
    • WIndows

      • 在 Windows 上安装 allure,首先要安装 Scoop,Scoop 的安装步骤如下:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        # 以Win10为例:
        1. 使用快捷键 Win + R 调起运行提示框
        2. 输入“cmd”进入到命令行
        3. 输入“powershell”进入到powershell模式(此时你的命令提示应该以PS开始)
        4. 确保你的PowerShell版本大于5.0,命令如下:
        $psversiontable.psversion.major # 这个运行后出现的值应该>=5.0
        5. 允许PowerShell 执行本地脚本:
        set-executionpolicy remotesigned -scope currentuser
        6. 安装Scoop
        Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
      • 安装好 Scoop 后,不要关闭 powershell,直接输入如下命令安装 allure:

        1
        PS C:\Users\Admin>scoop install allure
      • 如果你之前安装过 allure,也可以通过如下方式更新:

        1
        PS C:\Users\Admin>scoop update allure
      • 查看当前使用的 allure 版本:

        1
        PS C:\Users\Admin>allure --version
  • 本地安装完成allure,还需Python安装allure的模块

    1
    pip install allure-pytest
  • 执行 pytest 命令,并指定 allure 报告目录

    • 可以直接在命令行里执行

      1
      pytest --alluredir ./report/allure_raw
  • 在程序里执行

    1
    2
    pytest.main(["-m", "smoke", 
    "--alluredir=./allure_reports"])
  • 执行完成后,在当前目录下,report目录会生成一个allure_raw的原始文件,这个只是测试报告的原始文件,不能打开成html的报告

    image-20201224155112439

  • 打开html的报告需要启动allure服务,启动命令如下

    1
    allure serve report/allure_raw

    image-20201224155156051

  • 测试报告如下:

    image-20201224155242218