Allure-pytest功能特性介绍-程序员宅基地

技术标签: python  测试  json  

前言

Allure框架是一个灵活的轻量级多语言测试报告工具,它不仅以web的方式展示了简介的测试结果,而且允许参与开发过程的每个人从日常执行的测试中最大限度的提取有用信息
从dev/qa的角度来看,Allure报告简化了常见缺陷的统计:失败的测试可以分为bug和被中断的测试,还可以配置日志、步骤、fixture、附件、计时、执行历史以及与TMS和BUG管理系统集成,所以,通过以上配置,所有负责的开发人员和测试人员可以尽可能的掌握测试信息。
从管理者的角度来看,Allure提供了一个清晰的“大图”,其中包括已覆盖的特性、缺陷聚集的位置、执行时间轴的外观以及许多其他方便的事情。allure的模块化和可扩展性保证了您总是能够对某些东西进行微调,使Allure更适合您,那么今天我们就来说说如何使报告更加详细的显示我们需要的信息,以及allure与jenkins的集成

生成报告

pytest框架编写的项目如何生成测试报告,这里将不再讲解,具体过程可以参考:pytest进阶之html测试报告

注意:python使用的allure插件为allure-pytest

测试代码

为了大家能够快速的认识allure的所有功能特性,附上完整的测试代码

"""
------------------------------------
@Time : 2019/8/28 19:50
@Auth : linux超
@File : conftest.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : 2817404[email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest
import allure



@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
    outcome = yield
    report = outcome.get_result()
    report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")  # 解决乱码


@allure.step("打开浏览器")
def fixture_step():
    pass


@pytest.fixture
def init_url():
    fixture_step()
    yield True
conftest.py
"""
------------------------------------
@Time : 2019/9/4 21:05
@Auth : linux超
@File : test_allure_feature.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : 2817404[email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest
import allure
import os


def login(username=None, password=None):
    """模拟登录"""
    user = "linux超"
    pwd = "123456"
    if user == username and pwd == password:
        return {
     "code": 1001, "msg": "登录成功", "data": None}
    elif "" == password or password is None and username:
        return {
     "code": 1002, "msg": "密码不能为空", "data": None}
    elif "" == username or username is None and password:
        return {
     "code": 1003, "msg": "用户名不能为空", "data": None}
    else:
        return {
     "code": 1004, "msg": "用户名或密码错误", "data": None}


@allure.step("输入用户名")
def input_username(user):
    print("输入用户名")
    return user


@allure.step("输入密码")
def input_password(pwd):
    print("输入密码")
    return pwd


login_success_data = [
    # 测试数据
    {
        "case": "用户名正确, 密码正确",
        "user": "linux超",
        "pwd": "123456",
        "expected": {
     "code": 1001, "msg": "登录成功", "data": None}
    }
]

login_fail_data = [
    {
        "case": "用户名正确, 密码为空",
        "user": "linux超",
        "pwd": "",
        "expected": {
     "code": 1002, "msg": "密码不能为空", "data": None}
    },
    {
        "case": "用户名为空, 密码正确",
        "user": "",
        "pwd": "linux超哥",
        "expected": {
     "code": 1003, "msg": "用户名不能为空", "data": None}
    },
    {
        "case": "用户名错误, 密码错误",
        "user": "linux",
        "pwd": "linux",
        "expected": {
     "code": 1004, "msg": "用户名或密码错误", "data": None}
    }
]

username_none = [
    {
        "case": "缺省用户名参数",
        "pwd": "123456",
        "expected": {
     "code": 1003, "msg": "用户名不能为空", "data": None}
    }
]
password_none = [
    {
        "case": "缺省密码参数",
        "user": "linux超",
        "expected": {
     "code": 1002, "msg": "密码不能为空", "data": None}
    }
]
# 改变输出结果
ids_login_success_data = [
    "测试{}用户名:{}密码{}期望值{}".
        format(data["case"], data["user"], data["pwd"], data["expected"]) for data in login_success_data
]
ids_login_fail_data = [
    "测试{}用户名:{}密码{}期望值{}".
        format(data["case"], data["user"], data["pwd"], data["expected"]) for data in login_fail_data
]
ids_username_none = [
    "测试{}密码{}期望值{}".
        format(data["case"], data["pwd"], data["expected"]) for data in username_none
]
ids_password_none = [
    "测试{}用户名:{}期望值{}".
        format(data["case"], data["user"], data["expected"]) for data in password_none
]


@allure.feature("登录模块")
class TestLogin(object):

    @allure.severity(allure.severity_level.BLOCKER)
    @allure.story("测试登录成功")
    @allure.title("登录成功场景-{data}")
    @pytest.mark.parametrize("data", login_success_data, ids=ids_login_success_data)
    def test_login_success(self, data):
        """测试登录成功"""
        user = input_username(data["user"])
        pwd = input_password(data["pwd"])
        result = login(user, pwd)
        assert result == data["expected"]

    @allure.severity(allure.severity_level.CRITICAL)
    @allure.story("测试登录失败")
    @pytest.mark.parametrize("data", login_fail_data, ids=ids_login_fail_data)
    def test_login_fail(self, data):
        """测试用户名或密码错误"""
        user = input_username(data["user"])
        pwd = input_password(data["pwd"])
        result = login(user, pwd)
        assert result == data["expected"]

    @allure.severity(allure.severity_level.MINOR)
    @allure.story("测试用户名参数缺失")
    @pytest.mark.parametrize("data", username_none, ids=ids_username_none)
    def test_username_none(self, data):
        """测试缺省用户名"""
        pwd = input_password(data["pwd"])
        result = login(password=pwd)
        assert result == data["expected"]

    @allure.severity(allure.severity_level.MINOR)
    @allure.story("测试密码参数缺失")
    @pytest.mark.parametrize("data", password_none, ids=ids_password_none)
    def test_password_none(self, data):
        """测试缺省密码"""
        user = input_username(data["user"])
        result = login(username=user)
        assert result == data["expected"]

    @allure.severity(allure.severity_level.MINOR)
    @allure.story("测试初始化地址")
    @allure.testcase("https://www.cnblogs.com/linuxchao/", "测试用例地址")
    def test_init_url(self, init_url):
        flag = init_url
        assert flag is True

    @allure.severity(allure.severity_level.NORMAL)
    @allure.story("测试失败用例与用例中添加附件")
    @allure.link("https://www.cnblogs.com/linuxchao/", name="bug链接")
    @allure.description("这是一个一直执行失败的测试用例")
    def test_failed(self):
        """你也可以在这里添加用例的描述信息,但是会被allure.description覆盖"""
        try:
            assert False
        except AssertionError as e:
            with open("attach.png", "rb") as f:
                context = f.read()
                allure.attach(context, "错误图片", attachment_type=allure.attachment_type.PNG)
            raise e

    @allure.severity(allure.severity_level.TRIVIAL)
    @allure.story("测试broken用例")
    @allure.issue("https://www.cnblogs.com/linuxchao/", "错误链接")
    def test_broken(self):
        """broken"""
        with open("broken.json", "r", encoding='utf8') as f:
            f.read()

    @allure.severity(allure.severity_level.TRIVIAL)
    @allure.story("测试无条件跳过测试用例")
    @pytest.mark.skip(reason="无条件跳过")
    def test_skip(self):
        """skip"""
        pass


if __name__ == '__main__':
    pytest.main(["-vsq",
                 "--alluredir", "./allure-results", ])
    os.system(r"allure generate --clean ./allure-results -o ./allure-report")
test_allure_feature.py
"""
------------------------------------
@Time : 2019/8/28 19:45
@Auth : linux超
@File : test_allure_fixture.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : 2817404[email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest
import os
import allure


def function_scope_step():
    print("function_scope_step")


def class_scope_step():
    print("class_scope_step")


def module_scope_step():
    print("module_scope_step")


def session_scope_step():
    print("session_scope_step")


def step_inside_test_body():
    print("step_inside_test_body")


@pytest.fixture(params=[True, False], ids=['param_true', 'param_false'])
def function_scope_fixture_with_finalizer(request):
    if request.param:
        print('True')
    else:
        print('False')

    def function_scope_finalizer():
        function_scope_step()

    request.addfinalizer(function_scope_finalizer)


@pytest.fixture(scope='class')
def class_scope_fixture_with_finalizer(request):
    def class_finalizer_fixture():
        class_scope_step()

    request.addfinalizer(class_finalizer_fixture)


@pytest.fixture(scope='module')
def module_scope_fixture_with_finalizer(request):
    def module_finalizer_fixture():
        module_scope_step()

    request.addfinalizer(module_finalizer_fixture)


@pytest.fixture(scope='session')
def session_scope_fixture_with_finalizer(request):
    def session_finalizer_fixture():
        session_scope_step()

    request.addfinalizer(session_finalizer_fixture)


@allure.severity(allure.severity_level.BLOCKER)
@allure.feature("fixture场景")
class TestClass(object):

    def test_with_scoped_finalizers(self,
                                    function_scope_fixture_with_finalizer,
                                    class_scope_fixture_with_finalizer,
                                    module_scope_fixture_with_finalizer,
                                    session_scope_fixture_with_finalizer):
        step_inside_test_body()


if __name__ == '__main__':
    pytest.main(["-vsq",
                 "--alluredir", "./allure-results", ])
    os.system(r"allure generate --clean ./allure-results -o ./allure-report")
test_allure_fixture.py
[
  {
    "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
Browser=Chrome
Browser.Version=63.0
Stand=Production
ApiUrl=127.0.0.1/login
python.Version=3.6
environment.properties
"""
------------------------------------
@Time : 2019/9/3 14:21
@Auth : linux超
@File : run.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : 2817404[email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest
import os


if __name__ == '__main__':
    pytest.main(["-sq",
                 "--alluredir", "./allure-results"])
    os.system(r"allure generate --clean allure-results -o allure-report")
run.py

目录结构

Allure特性

Environment

在Allure报告中添加环境信息,通过创建environment.properties或者environment.xml文件,并把文件存放到allure-results(这个目录是生成最后的html报告之前,生成依赖文件的目录)目录下

environment.properties

Browser=Chrome
Browser.Version=63.0
Stand=Production
ApiUrl=127.0.0.1/login
python.Version=3.6

或者

environment.xml

<environment>
    <parameter>
        <key>Browser</key>
        <value>Chrome</value>
    </parameter>
    <parameter>
        <key>Browser.Version</key>
        <value>63.0</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.6</value>
    </parameter>
</environment>

执行run.py查看报告

Categories

测试报告默认统计两种类型的测试用例结果,失败的用例和故障测试用例,我们可以自定义添加用例的统计类型,同样需要在allure-results目录下新建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"]
  }
]

执行run.py查看报告

Fixtures and Finalizers

Fixtures和Finalizers是pytest在测试开始和测试结束调用的方法,allure会自动跟踪每一个fixture的调用,并且详细显示会调用哪些fixture和参数,而且会保留正确的调用顺数

测试代码

test_allure_html.py

def function_scope_step():
    print("function_scope_step")


def class_scope_step():
    print("class_scope_step")


def module_scope_step():
    print("module_scope_step")


def session_scope_step():
    print("session_scope_step")


def step_inside_test_body():
    print("step_inside_test_body")


@pytest.fixture(params=[True, False], ids=['param_true', 'param_false'])
def function_scope_fixture_with_finalizer(request):
    if request.param:
        print('True')
    else:
        print('False')

    def function_scope_finalizer():
        function_scope_step()

    request.addfinalizer(function_scope_finalizer)


@pytest.fixture(scope='class')
def class_scope_fixture_with_finalizer(request):
    def class_finalizer_fixture():
        class_scope_step()

    request.addfinalizer(class_finalizer_fixture)


@pytest.fixture(scope='module')
def module_scope_fixture_with_finalizer(request):
    def module_finalizer_fixture():
        module_scope_step()

    request.addfinalizer(module_finalizer_fixture)


@pytest.fixture(scope='session')
def session_scope_fixture_with_finalizer(request):
    def session_finalizer_fixture():
        session_scope_step()

    request.addfinalizer(session_finalizer_fixture)


class TestClass(object):

    def test_with_scoped_finalizers(self,
                                    function_scope_fixture_with_finalizer,
                                    class_scope_fixture_with_finalizer,
                                    module_scope_fixture_with_finalizer,
                                    session_scope_fixture_with_finalizer):
        step_inside_test_body()

执行run.py查看报告

@allure.step

pytest支持使用@allure.step修饰某些测试用例中需要的函数,使测试用例在allure报告中能够更加详细的显示测试过程

测试代码

test_allure_feature.py文件中修改如下代码

@allure.step("输入用户名")  
def input_username():
    print("输入用户名")


@allure.step("输入密码")
def input_password():
    print("输入密码")

执行run.py查看报告

conftest.py

@allure.step修饰的测试步骤还支持在conftest.py文件中定义,作为fixture的步骤,现在我们在项目目录下新建conftest.py文件,写入如下代码

conftest.py

@allure.step("打开浏览器")
def fixture_step():
    pass


@pytest.fixture
def init_url():
    fixture_step()
    yield True

test_allure_feature.py文件中添加如下用例

    def test_init_url(self, init_url):
        flag = init_url
        assert flag == True

执行run.py查看报告

allure.attach

使用allure.attach可以给报告中添加文件,图片,log,html代码等等。 我们修改test_allure_feature.py中如下用例, 并在用例所在目录添加attach.png图片

    def test_failed(self):
        """failed"""
        try:
            assert False
        except AssertionError as e:
            with open("attach.png", "rb") as f:
                context = f.read()
                allure.attach(context, "错误图片", attachment_type=allure.attachment_type.PNG)
            raise e

执行run.py查看报告

@allure.description

如果你想在报告中展示测试用例的描述信息,那么你可以使用@allure.description(string)或者@allure.description_html(html代码)修饰你的测试用例,test_allure_feature.py文件修改如下代码

    @allure.description("这是一个一直执行失败的测试用例")
    def test_failed(self):
        """你也可以在这里添加用例的描述信息,但是会被allure.description覆盖"""
        try:
            assert False
        except AssertionError as e:
            with open("attach.png", "rb") as f:
                context = f.read()
                allure.attach(context, "错误图片", attachment_type=allure.attachment_type.PNG)
            raise e

执行run.py查看报告

@allure.title

使用allure.title(title)可以重命名测试用例在allure报告中的名称,test_allure_feature.py文件修改如下代码

    @allure.title("登录成功场景-{data}")
    @pytest.mark.parametrize("data", login_success_data, ids=ids_login_success_data)
    def test_login_success(self, data):
        """测试登录成功"""
        user = input_username(data["user"])
        pwd = input_password(data["pwd"])
        result = login(user, pwd)
        assert result == data["expected"]

@allure.link

@allure.testcase

@allure.issue

这三种特性都可以给测试用例添加一个链接,test_allure_feature.py文件修改如下代码

    @allure.testcase("https://www.cnblogs.com/linuxchao/", "测试用例地址")
    def test_init_url(self, init_url):
        flag = init_url
        assert flag == True

    @allure.link("https://www.cnblogs.com/linuxchao/", name="bug链接")
    @allure.description("这是一个一直执行失败的测试用例")
    def test_failed(self):
        """你也可以在这里添加用例的描述信息,但是会被allure.description覆盖"""
        try:
            assert False
        except AssertionError as e:
            with open("attach.png", "rb") as f:
                context = f.read()
                allure.attach(context, "错误图片", attachment_type=allure.attachment_type.PNG)
            raise e

    @allure.issue("https://www.cnblogs.com/linuxchao/", "错误链接")
    def test_broken(self):
        """broken"""
        with open("broken.json", "r", encoding='utf8') as f:
            f.read()

执行run.py查看报告

 

 

 

@allure.feature

@allure.story

feature和story被称为行为驱动标记,因为使用这个两个标记,通过报告可以更加清楚的掌握每个测试用例的功能和每个测试用例的测试场景

在test_allure_feature.py文件中的测试类使用@allure.feature修饰, 测试方法使用@allure.story修饰

执行run.py查看报告

 以上两种标记不仅仅能够在测试报告中显示,而且还可以使用命令执行指定的测试模块或者场景

@allure.severity

此标记用来标识测试用例或者测试类的级别,分为blocker,critical,normal,minor,trivial5个级别,下面们把测试用例按级别标记,并查看一下测试报告

总结

以上就是所有的allure-pytest插件在pytest中支持的大部分功能特性,也许整理的不是很详细,所以如果你想详细的了解具体的特性在报告中的效果,还需自己动手尝试一下,附上本文参考链接

https://docs.qameta.io/allure/

转载于:https://www.cnblogs.com/linuxchao/p/linuxchao-pytest-allure.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dengying1836/article/details/101401963

智能推荐

PHP 和 Web 端对称加密传输|JSEncrypt-程序员宅基地

前后端对称加密传输JSEncryptGit地址:https://github.com/travist/jsencrypt首先这个包 需要提前生成 公钥 私钥 和 依赖 openssl注意:​ 明文长度最大为公钥长度-11,假如我的公钥长度是128,那明文最长也就117​ 如果需要加密解密长文本,请看下一种前后端对称加解密方式​ 这个库是与openssl一起工作如何使用这个库:...

MySQL报错:This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME-程序员宅基地

最近使用MySQL5.6查询表的前几条数据,使用了limit语句,结果出现错误:This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME查询资料发现是内层select语句不能带有limit语句,如下:select t1.* from t1where t1.id in (select t.id from t

mysql5.0安装-程序员宅基地

MySQL5.0版本的安装图解教程是给新手学习的,当前mysql5.0.96是最新的稳定版本。mysql 下载地址 http://www.jb51.net/softs/2193.html下面的是MySQL安装的图解,用的可执行文件安装的,详细说明了一下!打开下载的mysql安装文件mysql-5.0.27-win32.zip,双击解压缩,运行“setup.exe”,出现如下界面

java.net.MalformedURLException: unknown protocol: c 这个错一般有两种原因导致: 1、URL协议、格式或者路径错误,...-程序员宅基地

java.net.MalformedURLException: unknown protocol: c这个错一般有两种原因导致:1、URL协议、格式或者路径错误, 好好检查下你程序中的代码如果是路径问题,最好不要包含中文路径,因为有时中文路径会乱码,导致无法识别...

java基础之IO流(字节流,字符流,对象输入输出)_字符流如何写入对象-程序员宅基地

IO流的分类:按照数据流向 站在内存角度输入流 读入数据输出流 写出数据按照数据类型字节流 可以读写任何类型的文件 比如音频 视频 文本文件字符流 只能读写文本文件1.字节输入流的继承体系(常用的)InputStream的功能概述read(byte[] b, int off, int len)从流中从off的位置开始读取len个字节的数据存储到b中,返..._字符流如何写入对象

(精华)2020年9月22日 微服务 Consul工具层封装和使用_consul封装-程序员宅基地

if (!(Test-Path -Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force}_consul封装

随便推点

github博客突然无法访问_使用GitHubPages搭建个人博客-程序员宅基地

GitHubPages是一个免费的静态网页托管服务,您可以使用 GitHubPages托管博客、项目官网等静态网页。相同的你也可以用GiteePages搭建,但是因为GiteePages需要企业版才能绑定自定义域名,所以建议使用GitHubPages。GitHubPages支持使用Jekyll、Hugo、Hexo编译静态资源,可以根据自己的需求选择。我这里选择使用Jekyll。安装Rub...

Java8 函数式编程学习--四大内置函数式接口-程序员宅基地

目录概述内容Consumer源码分析示例代码运行结果注意Supplier源码分析示例代码概述Lambda表达式的使用前提是函数式接口,为了便利Lambda表达式的使用,Java8提供了内置的四大函数式接口,四大函数式接口满足了程序设计的大部分情况,省去了使用Lambda表达式时写函数式接口的步骤。四大函数式接口分别为:Consumer、Supplier、Function、Predicate,..._四大内置函数式接口

linux使用yum安装Mongodb-程序员宅基地

1、创建仓库vi/etc/yum.repos.d/mongodb-org-3.4.repo2、编辑仓库,写入资源[mongodb-org-3.4]name=MongoDBRepositorybaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/gpgcheck=1enabled=1gpgkey=https://www.mongodb.org/static/pgp/server-..

5G 技术已经融入生活,作为一名程序员,会面临怎样的机遇和挑战?_5g的普及与程序员的发展-程序员宅基地

5G时刻已经到来,让我们一起来看看。我们即将面对的5G时刻!1983年,第一代移动通信1G全球开始商用,直至1989年,中国才开通第一代移动通信;1991年,全球步入2G时代,中国在1994年才开始2G商用;2001年,全球开始步入3G时代,中国3G从2009年开始商用;2010年,全球4G开始商用,中国到2013年才开始商用。到了2019年,中国5G和全球同步发展!而这历史性进展我们用了40 年。今年6月6日,工信部发放5G商用牌照,仅仅过了147天,10月31号,中国5G正式商用!10月_5g的普及与程序员的发展

java:跨语言通用虚拟机GraalVM-程序员宅基地

GraalVM 简介GraalVM 是一个跨语言的通用虚拟机,支持Java、Scala、Groovy、Kotlin 等基于 JVM 的语言,以及 C、C++ 等基于 LLVM 的语言,还支持其他像 JavaScript、Ruby、Python 和 R 语言等。另外,GraalVM可以通过前端的LLVM执行JVM上面的原生代码。GraalVM 1.0版本是基于JDK8的。GraalVM...