allure生成漂亮的测试报告

allure初识

什么是allure

Allure 是由Qameta Software团队开源的一款旨在于解决让每个人能更容易生成并更简洁阅读的测试报告框架。它支持大多数的测试框架,如:Pytest、TestNG等,简单易用便于集成。[ allure官方文档 ]

  • Allure Framework是一种灵活的轻量级多语言测试报告工具,不仅可以以简洁的Web报告形式非常简洁地显示已测试的内容,也允许参与开发过程的每个人从日常测试中提取最大程度的有用信息
  • 从开发/质量保证的角度来看,Allure报告可以缩短常见缺陷的生命周期:可以将测试失败划分为bug和损坏的测试,还可以配置log,step,fixture,attachments,timings,历史记录以及与TMS的集成以及Bug跟踪系统,因此负责任的开发人员和测试人员将掌握所有信息
  • 从管理人员的角度来看,Allure提供了一个清晰的“全局”,涵盖了已涵盖的功能,缺陷聚集的位置,执行时间表的外观以及许多其他方便的事情
  • Allure的模块化和可扩展性确保您始终能够微调某些东西,以使Allure更适合您
  • 唯一不足的就是,拓展功能需要在测试用例集上加装饰器

环境配置

!pip3 install allure-pytest -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
Collecting allure-pytest
  Downloading http://pypi.doubanio.com/packages/9a/e3/9cea2cf25d8822752f55c9df16f0d0ef54ca6b369e3ccd0f51737f5288d3/allure_pytest-2.8.13-py3-none-any.whl
Collecting allure-python-commons==2.8.13 (from allure-pytest)
  Downloading http://pypi.doubanio.com/packages/21/4c/3fd196d82a31487d8cfc50a423de1dd0fb20128709a9b74e1e3d462dfb19/allure_python_commons-2.8.13-py3-none-any.whl
Collecting six>=1.9.0 (from allure-pytest)
  Downloading http://pypi.doubanio.com/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Collecting pytest>=4.5.0 (from allure-pytest)
  Downloading http://pypi.doubanio.com/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
[K    100% |████████████████████████████████| 256kB 3.4MB/s ta 0:00:01
[?25hCollecting attrs>=16.0.0 (from allure-python-commons==2.8.13->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting pluggy>=0.4.0 (from allure-python-commons==2.8.13->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting more-itertools>=4.0.0 (from pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
[K    100% |████████████████████████████████| 51kB 1.8MB/s ta 0:00:011
[?25hCollecting py>=1.5.0 (from pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
[K    100% |████████████████████████████████| 92kB 1.5MB/s ta 0:00:011
[?25hCollecting wcwidth (from pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting packaging (from pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest>=4.5.0->allure-pytest)
  Downloading http://pypi.doubanio.com/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
[K    100% |████████████████████████████████| 71kB 1.3MB/s ta 0:00:011
[?25hInstalling collected packages: attrs, six, zipp, importlib-metadata, pluggy, allure-python-commons, more-itertools, py, wcwidth, pyparsing, packaging, pytest, allure-pytest
Successfully installed allure-pytest-2.8.13 allure-python-commons-2.8.13 attrs-19.3.0 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0

生成报告

  • 要使Allure能够在测试执行期间收集测试结果,只需添加 --alluredir 选项,并提供指向应存储结果的文件夹的路径
pytest -n auto --alluredir=allure
  • 要在测试完成后查看实际报告,需要使用Allure命令行来让测试结果生成报告
allure serve allure

allure 报告结构

  • Overview:总览
  • Categories:类别,默认是分了failed和error,凡是执行结果是其中一个的都会被归到类里面,可以通过这里快捷查看哪些用例是failed和error的
  • Suites:测试套件,就是所有用例的层级关系,可以根据package、module、类、方法来查找用例
  • Graphs:测试结果图形化,包括用例执行结果的分布图,优先级,耗时等
  • Timeline:可以看到测试用例精确的测试时序(执行顺序),包括执行时间
  • Behaviors:行为驱动,根据epic、feature、story来分组测试用例(后面会讲到)
  • Packages:这就是按照package、module来分组测试用例了

allure特性

environment

可以理解成环境变量参数,没有什么实际作用,个人觉得只是为了让别人知道本次测试的运行环境参数而已,显示啥都是自己定的;注意!!默认是没有的

如何添加enviroment:

通过创建environment.properties或者environment.xml文件,并把文件存放到allure-results(这个目录是生成最后的html报告之前,生成依赖文件的目录)目录下,就是 --alluredir 后面跟的目录

# enviroment.properties
Browser=Chrome
Browser.Version=81.0.4044.92
Stand=Production
ApiUrl=127.0.0.1/login
python.Version=3.7.2

# enviroment.xml
<environment>
    <parameter>
        <key>Browser</key>
        <value>Chrome</value>
    </parameter>
    <parameter>
        <key>Browser.Version</key>
        <value>81.0.4044.92</value>
    </parameter>
    <parameter>
        <key>Stand</key>
        <value>Production</value>
    </parameter>
        <parameter>
        <key>ApiUrl</key>
        <value>127.0.0.1/login</value>
    </parameter>
        <parameter>
        <key>python.Version</key>
        <value>3.7.2</value>
    </parameter>
</environment>

Categories

直译:分类

通俗理解:测试用例结果的分类

有两类缺陷:

  • Product defects 产品缺陷(测试结果:failed)
  • Test defects 测试缺陷(测试结果:error/broken)
    我们是可以创建自定义缺陷分类的,将 categories.json 文件添加到allure-results目录即可(和上面environment.properties放同一个目录)

参数含义:

  • name:分类名称
  • matchedStatuses:测试用例的运行状态,默认["failed", "broken", "passed", "skipped", "unknown"]
  • messageRegex:测试用例运行的错误信息,默认是 .* ,是通过正则去匹配的哦!
  • traceRegex:测试用例运行的错误堆栈信息,默认是 .* ,也是通过正则去匹配的哦!
# categories.json
[
  {
    "name": "Ignored tests", 
    "matchedStatuses": ["skipped"] 
  },
  {
    "name": "Infrastructure problems",
    "matchedStatuses": ["broken", "failed"],
    "messageRegex": ".*bye-bye.*" 
  },
  {
    "name": "Outdated tests",
    "matchedStatuses": ["broken"],
    "traceRegex": ".*FileNotFoundException.*" 
  },
  {
    "name": "Product defects",
    "matchedStatuses": ["failed"]
  },
  {
    "name": "Test defects",
    "matchedStatuses": ["broken"]
  }
]

Flaky test

用法:在类或者方法上直接加 @Flaky ;官方也说了:可以将整个测试类标记为Flaky

含义:

  • 简单来说就是,不够稳定的测试用例集,有可能前阵子还运行成功,过阵子就运行失败,理解成“闪烁”
  • 标记成Flaky的好处就是:当用例失败的情况下,我们能获取足够详细的信息,毕竟有可能某些测试用例是非常重要的
  • 如果不标记为Flaky的话,可能就要禁用这些测试

allure.step()

allure除了支持pytest自带的特性之外(fixture、parametrize、xfail、skip),自己本身也有强大的特性可以在pytest中使用

@allure.step的作用

  • allure报告最重要的一点是,它允许对每个测试用例进行非常详细的步骤说明
  • 通过 @allure.step() 装饰器,可以让测试用例在allure报告中显示更详细的测试过程

注意点

  • step() 只有一个参数,就是title,你传什么,在allure上就显示什么
  • 可以像python字符串一样,支持位置参数和关键字参数 {0},{arg2},可看第四步那里,如果函数的参数没有匹配成功就会报错哦
  • step() 的使用场景,给我感觉就是,当方法之间嵌套会比较有用,否则的话只会显示一个步骤,类似下面图
"""要使Allure能够在测试执行期间收集测试结果,只需添加 --alluredir 选项,并提供指向应存储结果的文件夹的路径"""
!pytest allure/allure_step.py -n auto --alluredir=allure/allure_json
[1m============================= test session starts ==============================[0m
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: allure-pytest-2.8.13, xdist-1.31.0, rerunfailures-9.0, html-2.1.1, assume-2.2.1, forked-1.1.3, metadata-1.8.0, repeat-0.8.0
gw0 [1][0m[1m[1m
[32m.[0m[32m                                                                        [100%][0m
[32m============================== [32m[1m1 passed[0m[32m in 0.64s[0m[32m ===============================[0m
"""生成报告"""
!allure serve allure/allure_json/

allure.attach

作用:allure报告还支持显示许多不同类型的附件,可以补充测试结果;自己想输出啥就输出啥,挺好的

**======语法一=**

语法: allure.attach(body, name, attachment_type, extension)

参数列表:

  • body:要显示的内容(附件)
  • name:附件名字
  • attachment_type:附件类型,是 allure.attachment_type 里面的其中一种
  • extension:附件的扩展名(比较少用)

allure.attachment_type提供了哪些附件类型?

TEXT = ("text/plain", "txt")
    CSV = ("text/csv", "csv")
    TSV = ("text/tab-separated-values", "tsv")
    URI_LIST = ("text/uri-list", "uri")
    HTML = ("text/html", "html")
    
    XML = ("application/xml", "xml")
    JSON = ("application/json", "json")
    YAML = ("application/yaml", "yaml")
    PCAP = ("application/vnd.tcpdump.pcap", "pacp")
    PDF = ("application/pdf", "pdf")
    
    PNG = ("image/png", "png")
    JPG = ("image/jpg", "jpg")
    SVG = ("image/svg-xml", "svg")
    GIF = ("image/gif", "gif")
    BMP = ("image/bmp", "bmp")
    TIFF = ("image/tiff", "tiff")
    
    MP4 = ("image/mp4", "mp4")
    OGG = ("image/ogg", "ogg")
    WEBM = ("image/webm", "webm")

==语法二

语法二allure.attach.file(source, name, attachment_type, extension)

参数列表:

  • source:文件路径,相当于传一个文件
  • name:附件名字
  • attachment_type:附件类型,是 allure.attachment_type中的其中一种
  • extension:附件的扩展名(比较少用)
import allure
import pytest


@pytest.fixture
def attach_file_in_module_scope_fixture_with_finalizer(request):
    allure.attach(‘在fixture前置操作里面添加一个附件txt‘, ‘fixture前置附件‘, allure.attachment_type.TEXT)

    def finalizer_module_scope_fixture():
        allure.attach(‘在fixture后置操作里面添加一个附件txt‘, ‘fixture后置附件‘,
                      allure.attachment_type.TEXT)

    request.addfinalizer(finalizer_module_scope_fixture)


def test_with_attacments_in_fixture_and_finalizer(attach_file_in_module_scope_fixture_with_finalizer):
    pass


def test_multiple_attachments():
    allure.attach(‘<head></head><body> 一个HTML页面 </body>‘, ‘Attach with HTML type‘, allure.attachment_type.HTML)
    allure.attach.file(‘./reports.html‘, attachment_type=allure.attachment_type.HTML)

allure.description()

作用:可以添加足够详细的测试用例描述,以便于管理层查看

语法格式

  • @allure.description(str)
  • 在测试用例函数声明下方添加""" """
  • @allure.description_html(str):相当于传一个HTML代码组成的字符串,类似allure.attach()中传HTML
import allure

# 方式一
@allure.description("""
这是一个@allure.description装饰器
没有特别的用处
""")
def test_description_from_decorator():
    assert 42 == int(6 * 7)

# 方式二
def test_unicode_in_docstring_description():
    """
    当然,在方法声明的下一行这样子写,也算一种添加description的方式哦
    """
    assert 42 == int(6 * 7)

# 方式三
@allure.description_html("""
<h1>Test with some complicated html description</h1>
<table style="width:100%">
  <tr>
    <th>Firstname</th>
    <th>Lastname</th>
  </tr>
  <tr align="center">
    <td>William</td>
    <td>Smith</td>
</table>
""")
def test_html_description():
    assert True
"""收集测试数据及测试范围"""
!pytest allure/allure_description.py
[1m============================= test session starts ==============================[0m
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: allure-pytest-2.8.13, xdist-1.31.0, rerunfailures-9.0, html-2.1.1, assume-2.2.1, forked-1.1.3, metadata-1.8.0, repeat-0.8.0
collected 3 items                                                              [0m

allure/allure_description.py [32m.[0m[32m.[0m[32m.[0m[32m                                         [100%][0m

[32m============================== [32m[1m3 passed[0m[32m in 0.02s[0m[32m ===============================[0m
!allure serve allure/
Generating report to temp directory...
Report successfully generated to /tmp/2532045504637552864/allure-report
Starting web server...
2020-04-26 10:37:47.823:INFO::main: Logging initialized @5243ms to org.eclipse.jetty.util.log.StdErrLog
Can not open browser because this capability is not supported on your platform. You can use the link below to open the report manually.
Server started at <http://127.0.0.1:33287/>. Press <Ctrl+C> to exit

allure.title()

作用

  • 使得测试用例的标题更具有可读性,毕竟我们可以写成中文
  • 支持占位符传递关键字参数哦
  • 如果没有添加 @allure.title() 的话,测试用例的标题默认就是函数名,这样的可读性不高,毕竟咱们是中国人,显示中文title还是很有必要的
import pytest, allure


@allure.title("前置操作:登录")
@pytest.fixture
def test_loginss(request):
    params = request.param
    name = params["username"]
    pwd = params["pwd"]
    allure.attach(f"这是测试用例传的参数{params}")
    print(name, pwd, params)
    yield name, pwd


@allure.title("成功登录,测试数据是:{test_loginss}")
@pytest.mark.parametrize("test_loginss", [
    {"username": "name1", "pwd": "pwd1"},
    {"username": "name2", "pwd": "pwd2"}], indirect=True)
def test_success_login(test_loginss):
    name, pwd = test_loginss
    allure.attach(f"账号{name},密码{pwd}")
    
@allure.title("多个参数{name},{phone},{age}")
@pytest.mark.parametrize("name,phone,age", [
    (1, 2, 3),
    (4, 5, 6),
    (7, 8, 9)
])
def test_test_test(name, phone, age):
    print(name, phone, age)
"""收集测试范围与测试数据"""
!pytest allure/allure_title.py
"""生成测试报告"""
!allure server allure/allure_json

allure链接

先看 3 个装饰器

def link(url, link_type=LinkType.LINK, name=None):
    return safely(plugin_manager.hook.decorate_as_link(url=url, link_type=link_type, name=name))


def issue(url, name=None):
    return link(url, link_type=LinkType.ISSUE, name=name)


def testcase(url, name=None):
    return link(url, link_type=LinkType.TEST_CASE, name=name)

三个装饰器的区别:

  • issue()和testcase()其实调用的也是link(),只是link_type不一样
  • 必传参数 url:跳转的链接
  • 可选参数 name:显示在allure报告的名字,如果不传就是显示完整的链接;建议传!!不然可读性不高
  • 可以理解成:三个方法是一样的,我们都提供跳转链接和名字,只是链接的type不一样,最终显示出来的样式不一样而已【type不一样,样式不一样】
  • 如果你喜欢,只用@allure.link()也可以
  • 而出现三个装饰器的原因是为了更好地将链接分类【访问连接、Bug链接、测试用例链接】
import allure

TEST_CASE_LINK = ‘https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637‘


@allure.link(‘https://www.youtube.com/watch?v=4YYzUTYZRMU‘)
def test_with_link():
    pass


@allure.link(‘https://www.youtube.com/watch?v=Su5p2TqZxKU‘, name=‘点击我看一看youtube吧‘)
def test_with_named_link():
    pass


@allure.issue(‘140‘, ‘bug issue链接‘)
def test_with_issue_link():
    pass


@allure.testcase(TEST_CASE_LINK, ‘测试用例地址‘)
def test_with_testcase_link():
    pass
"""收集测试范围与测试数据"""
!pytest allure/allure_link.py
"""生成测试报告"""
!allure server allure/allure_json

allure标记

作用

  • 有时候我们写pytest的时候,会用到 @pytest.mark 但并不会显示在allure报告上
  • allure也提供了三种类型的标记装饰器,它们是可以显示在allure报告上的

allure标记装饰器分类

  • BDD样式的标记装饰器
  • 优先级(严重程度)标记装饰器
  • 自定义标记装饰器

BDD标记装饰器

提供了两个装饰器

  • @allure.feature
  • @allure.story

知识点:

  • story是feature的子集,当测试用例有 @allure.feature、@allure.story 时,在报告上会先显示feature,点开之后再显示story【可以想象成,安徒生童话(feature)有很多个童话故事(story)】
  • 如果不加 @allure.feature、@allure.story 时,在Behaviors栏目下,测试用例都不会分类显示,当用例多的时候可能看的花里胡哨

倘若是用pytest+allure写项目的话,又想用@pytest.mark.xxx 来自定义标记的话可以尝试用 @allure.feature、@allure.story 替换,毕竟可以显示在报告上

补充:
用命令行方式运行时,可以指定运行某个story或者feature:

pytest tests.py --allure-stories story_1,story_2

pytest tests.py --allure-features feature2 --allure-stories story2

import allure


def test_without_any_annotations_that_wont_be_executed():
    pass


@allure.story(‘epic_1‘)
def test_with_epic_1():
    pass


@allure.story(‘story_1‘)
def test_with_story_1():
    pass


@allure.story(‘story_2‘)
def test_with_story_2():
    pass


@allure.feature(‘feature_2‘)
@allure.story(‘story_2‘)
def test_with_story_2_and_feature_2():
    pass

allure.severity

作用:按严重性(优先级)来标记测试用例,它使用allure.severity_level枚举值作为参数

枚举严重程度常量:严重程度最高blocker,最低trivial

class Severity(str, Enum):
    BLOCKER = ‘blocker‘
    CRITICAL = ‘critical‘
    NORMAL = ‘normal‘
    MINOR = ‘minor‘
    TRIVIAL = ‘trivial‘

命令行方式:也可以通过命令行参数运行指定severity的测试用例
pytest tests.py --allure-severities normal,critical

import allure


def test_with_no_severity_label():
    pass


@allure.severity(allure.severity_level.TRIVIAL)
def test_with_trivial_severity():
    pass


@allure.severity(allure.severity_level.NORMAL)
def test_with_normal_severity():
    pass


@allure.severity(allure.severity_level.NORMAL)
class TestClassWithNormalSeverity(object):

    def test_inside_the_normal_severity_test_class(self):
        pass

    @allure.severity(allure.severity_level.CRITICAL)
    def test_inside_the_normal_severity_test_class_with_overriding_critical_severity(self):
        pass
print("test")
test