pytest框架进阶自学系列 | 定制测试报告

书籍来源:房荔枝 梁丽丽《pytest框架与自动化测试应用》

一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!

附上汇总贴:pytest框架进阶自学系列 | 汇总_热爱编程的通信人的博客-CSDN博客


除了具有pytest的环境外,Allure当前支持绝大多数可用功能。可以在代码中加入报告需要的信息,让报告更丰富且更易理解。

定制详细的步骤说明

Allure报告最显著的特点是,它可以使每个测试得到非常详细的说明。这可以通过@allure.step装饰器实现,该装饰器将带注释的方法或函数的调用与提供的参数一起添加到报表中。

  1. 步骤(step)可外部调用也可嵌套

带有注释的方法@step可以存储在测试之外,并在需要的时候导入。步骤方法可以具有任意深度的嵌套结构。在本章根目录下建立steps.py文件。

代码如下:

def imported_step():
    print("非常重要的步骤!")

同级建立test_step_nested.py文件,步骤可以直接加在测试方法上,也可以调用其他文件。如:在test_with_imported_step方法中调用steps文件中的imported_step方法。

同样可以嵌套使用,调用的方法参数随意。step_with_nested_steps方法嵌套调用nested_step方法再嵌套调用nested_step_with_arguments方法。

代码如下:

import allure
from steps import imported_step

@allure.step
def passing_step():
    print("通过的步骤")
    pass

@allure.step
def step_with_nested_steps():
    print("带有嵌套的步骤nested")
    nested_step()

@allure.step
def nested_step():
    print("调用带有参数arg的步骤")
    nested_step_with_arguments(1, 'abc')

@allure.step
def nested_step_with_arguments(arg1, arg2):
    print("带有两个参数:", arg1, arg2)
    pass

def test_with_imported_step():
    passing_step()
    print("外部导入")
    imported_step()

def test_with_nested_steps():
    passing_step()
    step_with_nested_steps()

在生成Allure报告时,每个步骤的状态都显示在名称右侧的“执行”中。嵌套步骤以树状可折叠结构组织

扫描二维码关注公众号,回复: 15689807 查看本文章
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6\test_step_nested.py 
Testing started at 14:39 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6\test_step_nested.py --no-header --no-summary -q in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6

============================= test session starts =============================
collecting ... collected 2 items

test_step_nested.py::test_with_imported_step PASSED                      [ 50%]通过的步骤
外部导入
非常重要的步骤!

test_step_nested.py::test_with_nested_steps PASSED                       [100%]通过的步骤
带有嵌套的步骤nested
调用带有参数arg的步骤
带有两个参数: 1 abc


============================== 2 passed in 0.03s ==============================

Process finished with exit code 0

  1. 步骤(step)可以有带参数的描述行

步骤可以具有一个描述行,该行支持传递位置和关键字参数的占位符。关键字参数的默认参数也将被捕获,这样在Allure报告的描述中就可以看到描述信息和具体的参数。

代码如下:

import allure

@allure.step('步骤可以有描述行,位置参数显示输入参数:"{0}",关键字显示输入参数:"{key}"')
def step_with_title_placeholders(arg1, key=None):
    pass

def test_steps_with_placeholders():
    step_with_title_placeholders(1, key='这是关键字参数')
    step_with_title_placeholders(2)
    step_with_title_placeholders(3, '这是位置参数')

  1. fixture和conftest同样支持步骤step

fixture也支持步骤,下面是使用conftest.py模块中定义的fixture进行测试的示例(即使不直接导入,这些fixture也将由pytest解析)。

在Allure的报告中初始化的步骤(在conftest.py中)显示在单独“前置”的树中,用于配置初始化和销毁。

代码如下:

conftest.py
import allure
import pytest

@allure.step('step in conftest.py')
def conftest_step():
    pass

@pytest.fixture
def fixture_with_conftest_step():
    conftest_step()
    
test_step_conftest_fixture.py
import allure

@allure.step
def passing_step():
    pass
def test_with_step_in_fixture_from_conftest(fixture_with_conftest_step):
    passing_step()

不同类型的附件补充测试说明

报告可以显示许多不同类型的附件,这些附件可以补充测试、步骤或fixture的结果。可以通过以下方式创建附件。

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

(1)body:要写入文件的原始内容。

(2)name:带有文件名的字符串。

(3)attachment_type:allure.attachment_type值之一。

(4)extension:用作创建文件的扩展名。

allure.attach.file(source,name,attachment_type,extension):source表示包含文件路径的字符串,其他参数与allure.attach的参数相同。

代码如下:

import allure
import pytest

@pytest.fixture
def attach_file_in_module_scope_fixture_with_finalizer(request):
    allure.attach('这个文本的附件是文件级的', '这可以是文件名字', allure.attachment_type.TEXT)
    def finalizer_module_scope_fixture():
        allure.attach('这个文本的附件是文件级的结束部分', '下面调用finalizer', 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):
    print("这是通过fixture调用上面的步骤,步骤中包含结束finalizer调用")
    pass

Allure报告中可以添加附件,附件的形式有多种,可以是图片、文本或HTML网页等。

代码如下:

import allure

def test_multiple_attachments():
    allure.attach.file('1.jpg', attachment_type=allure.attachment_type.JPG)
    allure.attach.file('2.yaml', attachment_type=allure.attachment_type.YAML)
    allure.attach('<head></head><body>这是个网页说明:下面可有多个</body>', '附加一个网页的具体说明', allure.attachment_type.HTML)

附件显示在它们所属的测试实体的上下文中。HTML类型的附件将呈现并显示在报告页面。这是一种方便的方式,可以为自己的测试结果提供一些自定义内容。

代码注释中所列的是可接受的所有类型

定制各种类型内容描述

可以添加测试的详细说明,也可以根据需要为报告提供尽可能多的上下文。这些内容可以通过几种方法来完成:可以添加@allure.description提供描述字符串的装饰器,也可以用@allure.description_html提供一些HTML,以便在测试用例的“描述”部分呈现,或者仅从测试方法的文档字符串中获取描述。

  1. 支持unicode字符串

可以使用不同国家的语言,以便使不同语言都可以正确显示。

代码如下:

import allure

def test_unicode_in_docstring_description():
    """unicode描述使用不同国家的语言
    hello my friend
    你好伙计 
    """
    assert 42 == int(6*7)

  1. 来自HTML的渲染description_html

可以添加html格式的描述,在报告中显示,没有一点违和感。

代码如下:

import allure

def test_unicode_in_docstring_description():
    """unicode描述使用不同国家的语言
    hello my friend
    你好伙计
    """
    assert 42 == int(6*7)

@allure.description_html("""
<h1>测试带有一些复杂的描述</h1>
<table style="width:100%">
  <tr>
    <th>名</th>
    <th>姓</th>
    <th>年龄</th>
  </tr>
  <tr align="center">
    <td>小明</td>
    <td>李</td>
    <td>50</td>
  </tr>
  <tr align="center">
    <td>小三</td>
    <td>唐</td>
    <td>94</td>
  </tr>
</table>
""")
def test_html_description():
    print("测试通过网页描述")
    assert True

@allure.description("""
这是一个多功能描述,计算
""")
def test_description_from_decorator():
    assert 42 == int(6*7)

  1. 动态描述,可替换开始时的描述

还可以从测试内动态更新最初的描述allure.dynamic.description,这是非常灵活的方式,代码如下:

import allure

def test_unicode_in_docstring_description():
    """unicode描述使用不同国家的语言
    hello my friend
    你好伙计
    """
    assert 42 == int(6*7)

@allure.description_html("""
<h1>测试带有一些复杂的描述</h1>
<table style="width:100%">
  <tr>
    <th>名</th>
    <th>姓</th>
    <th>年龄</th>
  </tr>
  <tr align="center">
    <td>小明</td>
    <td>李</td>
    <td>50</td>
  </tr>
  <tr align="center">
    <td>小三</td>
    <td>唐</td>
    <td>94</td>
  </tr>
</table>
""")
def test_html_description():
    print("测试通过网页描述")
    assert True

@allure.description("""
这是一个多功能描述,计算
""")
def test_description_from_decorator():
    assert 42 == int(6*7)
    
@allure.description("""
这段描述将在测试结束后被替换
""")
def test_dynamic_description():
    assert 42 == int(6*7)
    allure.dynamic.description('这是最后的描述,用于替换前面的描述!')

定制测试标题

可以使用特殊的@allure.title装饰器使测试标题更具可读性。标题支持参数的占位符并支持动态替换,也可以在“功能”中查看。

代码如下:

import allure
import pytest

@allure.title("这是一个正常的标题")
def test_with_a_title():
    assert 2 + 2 == 4

@allure.title("这个标题支持其他国家文字:xxx")
def test_with_unicode_title():
    assert 3 + 3 == 6
    
@allure.title("参数化标题:adding {param1} with {param2}")
@pytest.mark.parametrize('param1, param2, expected', [
    (2,2,4),
    (1,2,5)
])
def test_with_parameterized_title(param1, param2, expected):
    assert param1 + param2 == expected
    
@allure.title("动态标题")
def test_with_dynamic_title():
    assert 2 + 2 == 4
    allure.dynamic.title("替换动态标题为最终标题")

各种链接

报告可展示缺陷系统或测试管理系统链接,Allure具有整合@allure.link、@allure.issue和@allure.testcase描述的功能。通过这些功能可以直接链接到测试用例和缺陷管理系统及其他地方。

代码如下:

import allure

TEST_CASE_LINK = 'http://81.70.24.116:8088/zentao/bug-view-26.html'

@allure.link('http://81.70.24.116:8088/zentao/testcase-browse-1.html')
def test_with_link():
    pass

@allure.link('http://81.70.24.116:8088/zentao/product-index-no.html', name='单击进入项目')
def test_with_named_link():
    pass

@allure.issue('140', '测试出现问题的结果')
def test_with_issue_link():
    pass

@allure.testcase(TEST_CASE_LINK, '测试登录健壮性')
def test_with_testcase_link():
    pass

自定义各种标签

有时,需要灵活地执行要执行的测试。pytest允许使用标记装饰器@pytest.mark(pytest docs)。

Allure允许使用3种类型的标记修饰符以类似的方式标记测试,这些修饰符可以构造报告的结构形式:BDD样式标记,此标记可以表示模块、功能、故事、严重性标签、自定义标签。

代码如下:

BDD样式标记有两个装饰器:@allure.feature和@allure.story。用于根据特定项目的功能和故事来标记测试(有关背景,可参阅Wikipedia上的BDD文章)。要标记某些功能或故事属于子系统,可使用以epic_前缀开头的名称。

代码如下:

import allure

def test_without_any_annotations_that_wont_be_executed():
    pass

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

@allure.feature('feature_1')
def test_with_feature_1():
    pass

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

@allure.feature('feature_2')
@allure.story('story_1')
def test_with_story_1_and_feature_2():
    pass

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

可以使用以下命令行选项指定不同的测试集,以执行以逗号分隔的值的列表的操作:

在控制台输入不同的命令和参数会有不同的执行结果。pytest test_feature_story_epic.py --allure-stories story_1,story_2执行这个代码中@allure.story(story_1)和@allure.story(story_2)的测试方法。pytest test_feature_story_epic.py --allure-features feature_2 --allure-stories story_2中的两个参数是或的关系,也就是只要满足一个条件就执行,所以此处执行2个测试。

执行结果如下:

PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6> pytest .\test_feature_story_epic.py --allure-stories story_1,story_2 --alluredir=./result 
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pep8.py:110: FutureWarning: Possible nested set at position 1
  EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
Test session starts (platform: win32, Python 3.7.7, pytest 7.4.0, pytest-sugar 0.9.7)
Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
sensitiveurl: .*
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
plugins: allure-pytest-2.13.2, assume-2.4.3, base-url-2.0.0, cov-4.1.0, flakes-4.0.5, freezegun-0.4.2, html-3.2.0, httpserver-1.0.6, instafail-0.5.0, metadata-3.0.0, mock-3.11.1, ordering-0.6, pep8-1.0.1, picked-0.4.6, random-order-1.1.0, repeat-0.9.1, rerunfailures-12.0, selenium-4.0.1, sugar-0.9.7, timeout-2.1.0, variables-3.0.0
collected 6 items / 3 deselected / 3 selected                                                                                                                                                                                          

 src/chapter-6/test_feature_story_epic.py ✓✓✓                                                                                                                                                                            100% ██████████

Results (0.09s):
       3 passed
       3 deselected
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6>

严重性标记

要按严重性级别标记测试级别,可以使用@allure.severity装饰器。它以allure.severity_level枚举值作为参数。在这个枚举值中Bug的严重级别通常包括4~5级。在下面的代码注释中有相关解释。

代码如下:

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

严重性修饰符可以应用于函数、方法或整个类。

通过将--allure-severities命令行选项与逗号分隔的严重性级别结合使用,仅运行具有相应严重性的测试。这个级别的关系也是或的关系,只要有一条符合条件就执行。

执行结果如下:

PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6> pytest -s -v .\test_severity.py --allure-severities normal,critical --alluredir=/tmp/my_allure_results
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pep8.py:110: FutureWarning: Possible nested set at position 1
  EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
Test session starts (platform: win32, Python 3.7.7, pytest 7.4.0, pytest-sugar 0.9.7)
cachedir: .pytest_cache
metadata: {'Python': '3.7.7', 'Platform': 'Windows-10-10.0.22621-SP0', 'Packages': {'pytest': '7.4.0', 'pluggy': '0.13.1'}, 'Plugins': {'allure-pytest': '2.13.2', 'assume': '2.4.3', 'base-url': '2.0.0', 'cov': '4.1.0', 'flakes': '4.
0.5', 'freezegun': '0.4.2', 'html': '3.2.0', 'httpserver': '1.0.6', 'instafail': '0.5.0', 'metadata': '3.0.0', 'mock': '3.11.1', 'ordering': '0.6', 'pep8': '1.0.1', 'picked': '0.4.6', 'random-order': '1.1.0', 'repeat': '0.9.1', 'rerunfailures': '12.0', 'selenium': '4.0.1', 'sugar': '0.9.7', 'timeout': '2.1.0', 'variables': '3.0.0'}, 'Driver': None, 'Capabilities': {}}
Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
sensitiveurl: .*
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
plugins: allure-pytest-2.13.2, assume-2.4.3, base-url-2.0.0, cov-4.1.0, flakes-4.0.5, freezegun-0.4.2, html-3.2.0, httpserver-1.0.6, instafail-0.5.0, metadata-3.0.0, mock-3.11.1, ordering-0.6, pep8-1.0.1, picked-0.4.6, random-order-1.1.0, repeat-0.9.1, rerunfailures-12.0, selenium-4.0.1, sugar-0.9.7, timeout-2.1.0, variables-3.0.0
collected 5 items / 1 deselected / 4 selected                                                                                                                                                                                          

 src\chapter-6\test_severity.py::test_with_no_severity_label ✓                                                                                                                                                            25% ██▌       
 src\chapter-6\test_severity.py::test_with_normal_severity ✓                                                                                                                                                              50% █████     
 src\chapter-6\test_severity.py::TestClassWithNormalSeverity.test_inside_the_normal_severity_test_class ✓                                                                                                                 75% ███████▌  
 src\chapter-6\test_severity.py::TestClassWithNormalSeverity.test_inside_the_normal_severity_test_class_with_overriding_critical_severity ✓                                                                              100% ██████████

Results (0.09s):
       4 passed
       1 deselected
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-6>

重试信息展示

Allure允许汇总有关在一次测试运行中重新执行的测试信息,以及一段时间内测试执行的历史记录。对于重试,可以使用pytest重新运行失败插件。

例如,如果我们有一个非常不可靠的步骤方法,该方法经常失败,那么可以添加参数--reruns=5,pytest启动选项中指定参数后,我们会在Retries选项卡上看到所有未成功运行此测试的尝试。

代码如下:

import allure
import random
import time

@allure.step
def passing_step():
    pass

@allure.step
def flaky_broken_step():
    if random.randint(1,5) != 1:
        raise Exception('Broken!')
    
def test_broken_with_randomized_time():
    passing_step()
    time.sleep(random.randint(1,3))
    flaky_broken_step()

猜你喜欢

转载自blog.csdn.net/guolianggsta/article/details/131726988