详细讲解使用allure生成测试报告

测试报告应该包括哪些内容?

在我看来,测试报告至少需要包括项目总览和执行情况分析这两方面的信息。

1. 项目总览

本次测试执行了多长时间?

总共执行了多少测试用例?

有多少测试用例执行成功?

有多少测试用例执行失败?

有多少测试用例是非正常失败的?

不是由于断言失败,而是由于环境不稳定,运行中突发错误而导致的失败。

每条测试用例执行了多长时间?

本次测试在哪个环境运行?是开发环境、集成测试环境还是生产环境?

本次测试运行在哪个操作系统上?

2. 执行情况分析

针对每一次测试运行,测试报告应该提供最基本的测试分析,包括:

按照测试失败划分的测试分析图

按模块划分的测试分析图

按照测试用例重要程度、优先级划分的测试分析图

按照测试执行时间划分的测试分析图

这些不同维度的分析图,可用从各个层面来反映软件的质量情况。

所以,依托测试报告进行分析和总结,我们能够有理有据,以数字形式直观反映项目暴露出的质量问题,从而催促各个相关方后续改进。

在当前市面上所有第三方或者自研的测试报告系统中,Allure 是最全面,且支持的测试框架最多的一个测试报告系统。它是开源的测试报告框架,它旨在创建让团队每一个人都清楚明了的测试报告。下面我将以 Allure 报告为例,详细讲解测试报告的开发使用。

Allure 报告生成的原理

Allure 报告是基于标准的 xUnit 结果输出,再添加补充数据而生成的,其报告的生成基于如下两个步骤。

在测试执行期间,一个名为 Adapter 的小型 library 被连接到测试框架中,并将所有测试执行的信息保存到 XML 文件中。对于大多数编程语言下的流行测试框架(例如 python 语言中的 pytest,Java 中的 jUnit 等),Allure 都默认为其提供了 Adapter。

获取 XML 文件后,Allure 会将这些 XML 文件转换为 HTML 报告。这一步骤可以通过持续集成系统的 Allure 插件,或者命令行命令实现。

Allure 报告特点

Allure 报告之所以受到开发、测试,甚至管理人员的推崇,是因为它有如下明显的特点。

从开发/质量保证的角度看,Allure 报告可以缩短常见缺陷的生命周期。

可以将测试失败划分为 bug 和损坏的(Broken)测试,还可以配置日志、步骤、固定装置、附件、时间、历史记录,以及与 TMS 的集成和 Bug 跟踪系统,方便将 Task 与负责 Task 开发人员和测试人员绑定,从而使开发和测试人员第一时间掌握所有信息。

从管理者的角度看,Allure 提供了一个清晰的“全局”视野。

包括本次测试涵盖了哪些功能,Bug 在哪个 case 用例中被发现,以及整体测试用例、单条测试用例的执行时间等信息。

Allure 测试报告一览

Allure 测试报告除了涵盖测试运行的全面信息外,还提供各种维度的分析图,包括如下几个部分。

项目总览(OverView)

项目总览显示了总体测试运行的一系列统计信息。

按缺陷种类分析(Categories)

缺陷种类分析显示了所有不同原因引起的失败,并分类展示。

按测试套件分析(Suites)

测试套件分析显示了按照套件和类划分的所有的测试执行情况。

图表模块(Graphs)

图表模块,包括按照不同维度分析的各种图表(例如测试状态表分析,测试用例等级分析表,测试执行时间分析表等等)。

按执行时间分析(Timeline)

按执行时间分析模块,详细列出了各个测试用例的执行时间,你可以筛选出那些运行时间最长的测试用例进行优化。

针对 BDD 驱动的测试用例进行分析(Behaviors)

这里主要是根据 Epic、Feature 和 Story 标签对测试结果进行分组。

按照 Package 进行分析(Packages)

Package 模块列出了按照 Package 维度进行分析的详细图表。


了解了 Allure 测试报告的模块划分,我们来看下,如何配置使得这些模块反映测试的执行情况。

Allure 测试报告开发配置指南

Allure 有很多独有的功能,可用来方便自定义测试报告,下面我将详细为你讲解。

1.Allure 的安装和配置

在不同操作系统下安装 Allure 报告的方式各有不同。

你也可以直接通过如下方式安装:

pip install allure-pytest

注意,如果你安装过 Allure 2.0 之前的版本,你需要先将之前的版本卸载。

2.Allure 测试报告基础使用

Allure 测试报告安装配置好后,由 pytest 运行的测试便可以直接使用。假设当前你有个项目名为 allureDemo,则你可以采用如下方式使用 Allure 报告:

首先运行你的测试

# 直接执行allureDemo项目下的所有测试用例,并将测试报告文件夹allure_reports放在项目根目录下

D:\_Automation\allureDemo>pytest -s  -v --alluredir=./allure_results

运行完成后,打开 allure 报告

# 以Win10为例:

# 1. 使用快捷键 Win + R 调起运行提示框

# 2. 输入“cmd”进入到命令行

# 3. 切换目录到项目根目录,本例中在D:\_Automation\allureDemo>:

# 4. 输入命令生成allure报告:

allure serve allure_reports

此时,Allure 会帮你生成测试报告会自动打开。

Allure 报告实战

为了清晰地讲解 Allure 各个模块的使用,但又尽量少地引入其他代码,我将重新建立一个项目来详解 Allure 测试报告。

|--allureDemo

    |--tests

        |--test_baidu.py

        |--test_basic_report.py

        |--__init__.py

    |--conftest.py

其中 conftest.py 里的代码如下:

import allure

import pytest

def pytest_addoption(parser):

    parser.addoption(

        "--flag", action="store_true", default=False, help="set skip or not")

    parser.addoption(

        "--browser", action="store", default="Firefox", help="set browser")

@pytest.fixture(scope='session')

def get_flag(request):

    return request.config.getoption('--flag')

@pytest.fixture(scope='session')

def get_browser(request):

    return request.config.getoption('--browser')

@pytest.hookimpl(tryfirst=True, hookwrapper=True)

def pytest_runtest_makereport(item, call):

    """

      本hook用于制作测试报告

      :param item:测试用例对象

      :param call:测试用例的测试步骤

              执行完常规钩子函数返回的report报告有个属性叫report.when

                when=’setup’ 代表返回setup 的执行结果

                when=’call’ 代表返回call 的执行结果

      :return:

    """

    outcome = yield

    rep = outcome.get_result()

    if (rep.when == "call" or rep.when == 'setup') and (rep.failed or rep.skipped):

        try:

            if "initial_browser" in item.fixturenames:

                web_driver = item.funcargs['initial_browser']

            else:

                # 如果找不到driver,则直接return

                return

            allure.attach(web_driver.get_screenshot_as_png(), name="wrong picture",

                          attachment_type=allure.attachment_type.PNG)

        except Exception as e:

            print("failed to take screenshot".format(e))

在这个代码中,我分别定义了两个命令行参数 flag 和 browser。

flag:只有两个值 True 和 False,当用户不传 flag 参数时,默认为 False,当用户传递 flag 时,值为 True。

browser:代表要启用的浏览器,默认是 Firefox 浏览器。

对应地,get_flag 和 get_browser 这两个 fixture 就是分别用来取 flag 和 browser 的值。

被装饰器 @pytest.hookimpl(tryfirst=True, hookwrapper=True) 装饰的函数 pytest_runtest_makereport,是 pytest 提供的 Hook 函数,它有以下两个作用:

可以获取到测试用例不同执行阶段的结果(setup,call,teardown);

可以获取钩子方法的调用结果(yield 返回一个 result 对象)和调用结果的测试报告(返回一个 report 对象,即 _pytest.runner.TestReport)。

而在本例中,我通过它实现了当测试失败或者被 skip 时,自动根据 WebDriver 提供的 get_screenshot_as_png() 方法来截图。

接着,我们来看下文件 test_baidu.py 的内容:

# -*- coding: utf-8 -*-

import time

import allure

import pytest

from selenium import webdriver

@allure.epic('baidu')

@allure.description('测试百度的搜索功能')

@allure.severity('BLOCKER')

@allure.feature("百度搜索")

@allure.testcase("http://www.baidu.com")

@pytest.mark.baidu

class TestBaidu:

    @pytest.fixture

    def initial_browser(self, get_browser):

        if get_browser:

            if get_browser.lower() == "Chrome":

                self.driver = webdriver.Chrome()

            elif get_browser.lower() == "firefox":

                self.driver = webdriver.Firefox()

            else:

                self.driver = webdriver.Chrome()

        else:

            self.driver = webdriver.Chrome()

            self.driver.implicitly_wait(30)

        self.base_url = "http://www.baidu.com/"

        yield self.driver

        self.driver.quit()

    @allure.title("测试百度搜索正确")

    @pytest.mark.parametrize('search_string, expect_string', [('iTesting', 'iTesting'), ('helloqa.com', 'iTesting')])

    def test_baidu_search(self, initial_browser, search_string, expect_string):

        driver = initial_browser

        driver.get(self.base_url + "/")

        driver.find_element_by_id("kw").send_keys(search_string)

        driver.find_element_by_id("su").click()

        time.sleep(2)

        search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')

        print(search_results)

        assert (expect_string in search_results) is True

    @allure.title("测试百度搜索失败")

    @pytest.mark.parametrize('search_string, expect_string', [('iTesting', 'isGood')])

    def test_baidu_search_fail(self, initial_browser, search_string, expect_string):

        driver = initial_browser

        driver.get(self.base_url + "/")

        driver.find_element_by_id("kw").send_keys(search_string)

        driver.find_element_by_id("su").click()

        time.sleep(2)

        search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')

        assert (expect_string in search_results) is True

if __name__ == "__main__":

    pytest.main(["-m", "baidu", "-s", "-v", "-k", "test_baidu_search", "test_baidu_fixture_sample.py"])

在这个文件中,我创建了一个测试类 TestBaidu 和两个测试方法 test_baidu_search() 和 test_baidu_search_fail()。注意:这两个方法有很多关于 allure 的装饰器,例如 @allure.epic、 @allure.feature 等,它们分别代表什么含义我等下再讲,我们继续往下看。

最后,test_basic_report.py 的内容如下:

import allure

import pytest

from flaky import flaky

@allure.epic("演示下allure支持的测试")

@allure.description('测试模块1用来对模块1进行测试')

@allure.feature("测试模块1")

@allure.story("测试模块1_story1")

@allure.testcase("http://www.baidu.com")

@pytest.mark.basic

class TestBasic:

    @allure.step("测试步骤1 -- 判断登录成功")

    @allure.severity('BLOCKER')

    def test_login(self):

        """模拟成功的测试用例"""

        assert 1 == 1

    @flaky

    @allure.step("测试步骤2 -- 查询余额")

    @allure.severity("normal")

    def test_savings(self):

        """模拟失败的测试用例"""

        assert 1 == 0

    @allure.step("测试步骤2.1 -- 查询余额")

    @allure.severity("normal")

    def test_savings1(self):

        """模拟失败的测试用例"""

        assert 1 == 0

    @allure.description("调试用,不执行")

    def test_deposit_temp(self):

        """模拟skip的测试用例"""

        pytest.skip('调试用例,skip')

    @allure.issue("http://itesting.club", "此处之前有bug,bug号如上")

    @allure.step("测试步骤3 -- 取现")

    def test_deposit(self):

        raise Exception('oops')

    @pytest.mark.xfail(ccondition=lambda: True, reason='this test is expecting failure')

    def test_xfail_expected_failure(self):

        """被期望的失败"""

        assert False

    @pytest.mark.xfail(condition=lambda: True, reason='this test is expecting failure')

    def test_xfail_unexpected_pass(self):

        """期望失败,但是却成功,会被标记为不期望的成功"""

        assert True

    @allure.step("测试步骤4 -- teardown")

    def test_skip_by_triggered_condition(self, get_flag):

        if get_flag == True:

            pytest.skip("flag 是true时, skip掉此条用例")

在这个文件中,为了模拟到所有的测试运行情况,我人为定义了一些 pass 和 fail,以及 skip、xfail 的情况。

1.Allure 测试报告装饰器

在运行整个测试之前,我们来学习下 Allure 测试报告的各个装饰器及其作用:

关于每一个装饰器的具体用法,可以直接参考上文中的代码示例。

2.为 Allure 测试报告添加 Environment

现在,我来生成下测试报告:

# 在项目根目录下执行

# 本例中,根目录是D:\_Automation\allureDemo>

D:\_Automation\allureDemo>pytest -m baidu -s  -v --alluredir=./allure_results

执行成功后,通过如下命令打开测试报告:

allure serve ./allure_results

可以看到如下的结果。默认情况下,Allure 生成的报告是不带 Environment 信息的,如下图中“1.Environment 默认没有”所示。

那么如何让测试报告带上 Environment 信息呢?

首先,执行完测试用例后,创建文件 environment.properties,其内容格式如下:

Browser=Chrome

Browser.Version=86.0.4240

Environment=QA

注意:这里为 key=value 的格式。这个文件你可以通过编写相关函数动态获取每次执行时的真实值,然后写入 environment.properties 文件。这里为了方便,我直接 Hard Coded.

然后,把文件 environment.properties 拷贝到你在执行测试用例时设置的 allure 报告目录下,在本例中为 allure_results 这个目录。

最后,执行如下命令:

allure serve ./allure_results

你会发现 Environment 里出现了我们刚刚配置的值:

3.为 Allure 测试报告增加错误类型

在默认情况下,Allure 仅仅会列出以下两种类型的 Categories。

Product Defects(failed tests)

表示真正的测试执行失败,如果 Categories 里出现这个错误,通常表明测试用例最后的输出跟期望不符合,有 Bug 出现。

Test Defects(broken tests)

表示测试用例本身有问题导致的错误,如果 Categories 里出现这个错误,通常表明测试用例在执行过程中出错了,需要我们进一步调查原因。

如果你仔细观察 test_basic_report.py 里的代码,你可以看到,我们很多用例是要 skip,或者需要根据用户的传参来 skip 的,这些测试用例没有被反映到 Categories 里。

那么如何自定义 Categories 呢?

首先,创建名称为 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"]

  }

]

然后,把文件 categories.json 拷贝到你在执行测试用例时设置的 allure 报告目录下,在本例中为 allure_results 这个目录。

最后,执行如下命令:

allure serve ./allure_results

你会发现 Categories 里出现了我们刚刚配置的值 Ignored tests:

4.显示历次运行的 trends

同样地,默认生成的 Allure 报告不包括历次运行信息 Trends,如果想添加历次运行信息到Trends,步骤如下。

执行完测试后,不要执行 allure serve 命令,转而执行 allure generate。

allure generate ./allure_results

这个操作会生成一个新的文件夹,名为 allure-report。拷贝 allure-report 文件夹下的 history 文件夹,及其子文件夹到 allure_results 这个目录中。

在新的一次测试执行后执行 allure serve,即可把历史记录带到 Allure 报告中。

allure serve ./allure_results

执行完后,打开 Allure 测试报告,你将看到 Trend 的内容。

5.为 Allure 测试报告添加执行人

同样地,默认的 Allure 测试报告也不显示 Executor,这是因为 Executor 通常是由 Builder 自动生成的,比如通过 Jenkins pluginAllure Jenkins Plugin 来生成。

关于如何使用 Allure Jenkins Plugin 配置 Allure,你可以在课后另外了解。

当然你也可以自己生成, 首先创建名称为executor.json 的文件,内容如下:

{

  "name": "iTesting",

  "type": "jenkins",

  "url": "http://helloqa.com",

  "buildOrder": 3,

  "buildName": "allure-report_deploy#1",

  "buildUrl": "http://helloqa.com#1",

  "reportUrl": "http://helloqa.com#1/AllureReport",

  "reportName": "iTesting Allure Report"

}

然后,拷贝 executor.json 到 allure_results 这个目录中去。

最后, 执行如下命令即可:

allure serve ./allure_results

执行完后,打开 Allure 测试报告,你将看到 Executor 的信息:

6.Allure 测试报告实现错误自动截图

在测试时,特别是 UI 自动化测试错误发生时,我们会想通过系统截图的方式,来更清楚地了解系统当时的状态,于是错误截图就很必要。

使用 Allure 自动实现错误截图,可以参考下我在本讲 conftest.py 这个文件中定义的如下函数:

pytest_runtest_makereport

现在,我们来看下错误截图的实际效果。

首先,通过如下命令运行所有测试:

pytest -s  -v --alluredir=./allure_results --flag --browser chrome

执行成功后,通过如下命令打开测试报告:

allure serve ./allure_results

可以看到如下结果:

点击图中的 “FEATURES BY STORIES”, 进入 Behaviors 页面,展开后你可以看到下图:

可以看到,第 3 条测试用例执行失败了,截图信息也自动保存了。

7.通过 Allure 测试报告查看测试用例详情

Allures 生成后,我们可以通过点击左侧的导航栏,进入到不同的页面,观察 Allure 的不同维度的测试分析。这里我不再一一阐述,我重点分享一下如下页面:

你点击任何一条测试用例,都能在 OverView 里看到这个测试用例的详细情况,包括测试所属的模块、测试用例的重要程度,以及这个测试用例对应 Jira 的 story 等信息,非常清晰明了。

自研的测试报告

除了用 Allure 来生成测试报告外,你的测试报告也可以自研,那么自研怎么操作呢?

最简单的方式是你写一个 HTML 模板,然后把涉及运行信息的要素,比如运行环境、执行人、测试成功条数、测试失败条数定义为变量,然后在你的自研框架运行完后,把这些变量替换为真实的值即可。

至于图表的操作,你可以使用 hicharts、echarts 等图表可视化工具完成。因为这里涉及 HTML 还有一些前端的代码知识,我就不再详细介绍。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容