Pytest

Pytest之fixture的使用

fixture的功能

fixture是pytest特有的功能,用以在测试执行前和执行后进行必要的准备和清理工作。使用pytest.fixture标识,定义在函数前面。在你编写测试函数的时候,你可以将此函数名称做为传入参数,pytest将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。

主要的目的是为了提供一种可靠和可重复性的手段去运行那些最基本的测试内容。

从功能上看来,与setup、teardown相似,但是优势明显:

  • 命名方式灵活,不局限于setupteardown这几个命名
  • conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置
  • scope="module" 每一个.py文件调用一次
  • scope="session" 可以实现多个.py跨文件使用一个session来完成多个用例

fixture的参数详解

@pytest.fixture(scope = "function", params=None, autouse=False, ids=None, name=None)

scope
用于控制Fixture的作用范围

默认取值为function(函数级别),控制范围的排序为:session > module > class > function

  • function 函数级 每一个函数或方法都会调用
  • class 类级别 每个测试类只运行一次
  • module 模块级 每一个.py文件调用一次
  • session 会话级 每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法

params
params:一个可选的参数列表,它将导致对fixture函数和使用它的所有测试的多次调用。我们可以选择让fixture返回我们需要的东西。如果你的fixture需要配置一些数据,读个文件,或者连接一个数据库,那么可以使用fixture返回这些数据或资源。

如何带参数

fixture还可以带参数,可以把参数赋值给params,默认是None。对于param里面的每个值,fixture都会去调用执行一次,就像执行for循环一样把params里的值遍历一次。

autouse
为True的话就是开启,开启后范围的方法都会自动去执行。默认为False。

ids
字符串id的列表,每个id对应于参数,是测试id的一部分,如果没有提供id,它们将从参数自动生成标识。

name
可以理解成为前置函数取个别名。

关于各个参数的实例可看这篇文章:Pytest之Fixture参数详解及使用,例子很详细。

fixture的三种调用方式

测试用例内直接调用

1
2
3
4
5
6
7
8
import pytest

@pytest.fixture()
def test_01():
print("this is test_01")

def test_02(test_01):
print("this is test_02")

通过fixture decorator调用fixture

:如果一个方法或者一个class用例想要同时调用多个fixture,可以使用@pytest.mark.usefixtures进行叠加。注意叠加顺序,先执行的放底层,后执行的放上层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pytest

@pytest.fixture()
def test_01():
print("this is test_01")

# 第一种是每一个函数前声明
@pytest.mark.usefixtures("test_01")
def test_02():
print("this is test_02")

# 第二种是封装在类里,类里的每一个成员函数声明
class Test01:
@pytest.mark.usefixtures("test_01")
def test_03(self):
print("this is test_03")

@pytest.mark.usefixtures("test_01")
def test_04(self):
print("this is test_04")

# 第三种是在类前声明
@pytest.mark.usefixtures("test_01")
class Test2:
def test_05(self):
print("this is test_05")

def test_06(self):
print("this is test_06")

使用参数autouse调用fixture

autouse设置为True时,在一个session内的所有的test都会自动调用这个fixture。

fixture函数的返回值:return 和 yield 和 addfinalizer终结函数

主要介绍下yieldaddfinalizer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# yeild也是一种函数的返回值类型,是函数上下文管理器,使用yield被调fixture函数执行遇到yield会停止执行,接着执行调用的函数,调用的函数执行完后会继续执行fixture函数yield关键后面的代码。

```
# 如这段代码,会在请求方法执行完后调用fn函数
@pytest.fixture(scope="class")
def browser(request):
browser = webdriver.Edge()
yield browser
def fn():
logger.logging.info("全部用例执行完, teardown driver!")
# 后置:关闭浏览器
browser.quit()
request.addfinalizer(fn)
return browser
```

因此利用fixture函数,我们可以说pytest集合了setup、teardown,既做了初始化,又做了后置的清理工作。

Pytest之parametrize的使用

Pytest参数化有两种方式:

  • @pytest.fixture(params=[])
  • @pytest.mark.parametrize()

两者都会多次执行使用它的测试函数,但@pytest.mark.parametrize()使用方法更丰富一些

定义

1
@pytest.mark.parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None))

参数

参数说明
argnames必传,参数名, 以逗号分隔的字符串,表示一个或多个参数名称(key),或参数字符串的列表/元组
argvalues必传,参数值,若argnames有一个刚单值列表传入,若argnames有多个,以套用元组的列表展示,无组内与参数名一一对应
indirect为true时,那argnames一定是一个fixture函数名称,argvalues值将传入对应的fixture内,相当于@pytest.fixture(params=)的用法,默认False
ids标记子用例执行名称,与argvalues数量一致,未指定自动生成,默认None
scope如果指定,则表示参数的范围。范围用于按参数实例对测试进行分组。它还将覆盖任何fixture函数定义的范围,允许使用测试上下文或配置设置动态范围

参数使用举栗 - argnames、argvalues

单参数单值

1
2
3
4
5
6
7
8
import pytest

@pytest.mark.parametrize('arg',[1])
#测试函数要将argnames做为形参传入
def test_one_params(arg):
# arg = 1
print("传入的值为:{}".format(arg))
assert arg == 1

单参数多值

  • 单参数多值,argvalues可以传入多样的python数据类型:列表,嵌套了元组的列表,字典,字符串
  • 传入多个值时,测试用例会被执行多次,每次取一个值去运行
1
2
3
4
5
6
7
import pytest

@pytest.mark.parametrize('arg',['abc', 1, {'a':1, 'b':3}, (4,5)]
def test_one_params(arg):
# 依次执行:abc,1,{'a':1, 'b':3},(4,5)
print("传入的值为:{}".format(arg))
assert isinstance(arg,dict)

多参数多值

1
2
3
4
5
6
7
import pytest

@pytest.mark.parametrize("test_input, expected", [("3+5", 8),("5-2", 1),("5*2", 10)])
def test_params(test_input,expected):
# 原值:3+5,期望值8
print("原值:{} 期望值{}".format(test_input,expected))
assert eval(test_input) == expected

参数使用举栗 - indirect

  1. indirect一般与Pytest的request、fixture组合使用
  2. 当indrect 为True时,argnames则要传入fixture函数名称,不再是一个普通参数,而是要被调用的fixture函数,argvalues则是要给这个函数传的值
  3. 用法其实与@pytest.fixture(params)一样,但使用了@pytest.mark.parametrize相当于参数化了fixture,而不是只有固定的一套数据传入使用

indirect=True的用法与False用法基本是类似的,唯一的区别是,当它为True的时候会将参数传入fixture进行一次前置处理,然后将处理后的返回值再给测试函数使用,而False是直接使用。

单fixture单值(通过列表)

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

@pytest.fixture()
def login(request):
user = request.param
# 执行两次,分别是张三和李四
print("传入的用户名为:{}".format(user))
return user

user = ['张三','李四']

@pytest.mark.parametrize('login', user, indirect=True)
def test_one_param(login):
# 执行两次,分别是张三和李四
print("测试类的读到的用户是:{}".format(login))

单fixture多值(通过字典)

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

userinfo = [
{'user':'张三','pwd':123},
{'user':'李四','pwd':456}
]

@pytest.fixture()
def login(request):
user = request.param
# 执行两次,分别是张三/123和李四/456
print("传入的用户名为:{},密码为:{}".format(user['user'],user['pwd']))
return user

@pytest.mark.parametrize('login', userinfo, indirect=True)
def test_one_param(login):
# 执行两次,分别是张三/123和李四/456
print("测试类的读到的用户是:{} 密码是:{}".format(login['user'],login['pwd']))

传多fixture多值(通过嵌套元组的列表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pytest
# 一个@pytest.mark.parametrize使用多个fixture,传入的数据是嵌套了元组的列表
userinfo = [
('张三',123),
('李四','pwd')
]

@pytest.fixture()
def login_user(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user

@pytest.fixture()
def login_pwd(request):
pwd = request.param
print("密码为:{}".format(pwd))
return pwd

@pytest.mark.parametrize('login_user,login_pwd',userinfo,indirect=True)
def test_one_param(login_user,login_pwd):
print("测试类的读到的用户是:{} 密码是:{}".format(login_user,login_pwd))

叠加fixture(单值列表,执行次数笛卡尔集 N*M)

测试用例一共执行四次,也就是两个fixture传入值的积(笛卡尔集)

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

user = ['张三','李四']
pwd = [124,345]

@pytest.fixture()
def login_user(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user

@pytest.fixture()
def login_pwd(request):
pwd = request.param
print("密码为:{}".format(pwd))
return pwd

@pytest.mark.parametrize('login_pwd',pwd,indirect=True)
@pytest.mark.parametrize('login_user',user,indirect=True)
def test_one_param(login_user,login_pwd):
print("测试类的读到的用户是:{} 密码是:{}".format(login_user,login_pwd))

参数使用举栗 - ids

作用:标记子用例执行名称,增加可读性,中文需要转码:https://www.cnblogs.com/creamk87/p/13505605.htm

未加IDS之前执行结果:

加了IDS之后:

参数使用举栗 - scope

  1. scope的作用范围取值与fixture scope一致,当indirect=True才会被使用
  2. scope的作用范围会覆盖fixture的scope范围,如果同一个被调用的fixture有多个parametrize定义了scope,取第一条的范围,见下例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pytest
@pytest.fixture(scope='class')
def login_user(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user

@pytest.fixture(scope='class')
def login_pwd(request):
pwd = request.param
print("密码为:{}".format(pwd))
return pwd

class TestCase:
userinfo = [
('张三', 123)
]
ids = ["case{}".format(i) for i in range(len(userinfo))]

@pytest.mark.parametrize('login_user,login_pwd', userinfo, ids=ids, indirect=True, scope='function')
def test_one_param(self,login_user,login_pwd):
print("测试类的读到的内容是{}{}".format(login_user,login_pwd))


@pytest.mark.parametrize('login_user,login_pwd', userinfo, ids=ids, indirect=True, scope='function')
def test_one_param2(self,login_user,login_pwd):
print("测试类的读到的内容是{}{}".format(login_user,login_pwd))

小于Fixture的scope范围

运行结果:

大于Fixture的scope范围

多个SCOPE范围为先执行的

调整下顺序

参数使用举栗 - 与其它mark一起使用【标记子用例】

1
2
3
4
5
6
7
8
9
10
11
12
13
import pytest
a = '跳过'
@pytest.mark.parametrize("x,y",
[('test1',123456),
pytest.param("test1",123456,marks=pytest.mark.xfail),
pytest.param("test1",1256,marks=pytest.mark.xfail),
pytest.param("test2",123,marks=pytest.mark.skip("跳过这条用例")),
pytest.param("test3",456,marks=pytest.mark.skipif(a =='跳过',reason="有条件的跳过"))
]
)
def test_one(x,y):
assert x== 'test1'
assert y == 123456

运行结果

环境初始化与清理环境setup和teardown方式

方法级别初始化、清除,每个方法运行前后执行一次

setup 和 teardown,setup_method,teardown_method

类级别初始化、清除

在类中添加方法 def setup_class()def teardown_class()

在定义了该方法 def setup_class()def teardown_class() 的类中所有的用例执行前后只执行一次

模块级别初始化、清除

整个模块中所有类中的内容执行前后运行setup_moduleteardown_module,必须设置全局方法 def setup_module()def teardown_module()

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# -*- coding:utf-8 -*-
import pytest

def setup_module(self):
print('setup_module环境初始化')
def teardown_module(self):
print('teardown_module环境清除')

class Test1:
def setup_class(self):
print('setup_class环境初始化')
def teardown_class(self):
print('teardown_class环境清除')
def setup_method(self):
print('setup_method环境初始化')
def teardown_method(self):
print('teardown_method环境清除')
def setup(self):
print('setup环境初始化')
def teardown(self):
print('teardown清理环境')
def test_1(self):
assert 1 == 1
def test_2(self):
assert 1 == 1
def test_3(self):
assert 1 == 1

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setup_module环境初始化
setup_class环境初始化
setup_method环境初始化
setup环境初始化
PASSED [ 33%]teardown清理环境
teardown_method环境清除
setup_method环境初始化
setup环境初始化
PASSED [ 66%]teardown清理环境
teardown_method环境清除
setup_method环境初始化
setup环境初始化
PASSED [100%]teardown清理环境
teardown_method环境清除
teardown_class环境清除
teardown_module环境清除

执行pytest的方法

执行指定case

  • **pytest.main()**:执行所有case
  • **pytest.main(['-vs', 'TestCalc.py::TestCalc'])**:执行TestCalc.py文件内TestCalc类下所有case
  • **pytest.main(['-vs', 'TestCalc.py::TestCalc::test_division_seven'])**:执行TestCalc.py文件内TestCalc类下名字为test_division_seven的case
  • **pytest.main(['参数','xxx'])**:pytest.main()内必须是list格式,在低版本已不支持str

具体参数

  • **-v**:打印详细运行的日志信息

  • **-s: pytest -s xxx.py**:输出case中print的内容

  • -m:

    • pytest -m "tag名称" 运行指定tag名称的用例,也就是运行有@pytest.mark.[标记名]这个标记的case
    • pytest -m "not tag名称" 不运行指定tag名称的用例
    • pytest -m "webtest or slow" 运行webtestslow两个指定tag用例
  • **-k:pytest -k "类名、方法名、类名 and not 方法名"**:运行指定case的用例

  • **-x**:遇到失败的case就是停止执行

  • **--lf**:只重新运行上次运行失败的用例(或如果没有失败的话会全部跑)

  • **--ff**:运行所有测试,但首先运行上次运行失败的测试(这可能会重新测试,从而导致重复的fixture setup/teardown)

  • **--maxfail=num**:当用例失败个数达到num时,停止运行

  • **--collect-only**:收集测试用例,展示出哪些用例会被执行(只是展示不会执行case)

  • **--junit-xml:--junit-xml=path/name.xml**:在指定目录或当前目录下生成xml格式的报告(需要在pytest.ini文件内声明格式:junit_family=xunit2)

  • **--steup-show**:完整展示每个用例的fixture调用顺序

  • **--return**:失败后重试次数

  • **-n**:并发执行的线程数

注意:必须在pytest.ini文件内声明标签,不然则会告警PytestUnknownMarkWarning

1
2
3
4
[pytest]
markers =
div
tag

命令行执行

1
2
3
4
5
pytest test_quick_start.py --junit-xml=report.xml

main执行:
pytest.main(["-s", "TestCalc.py", "-m", "div", "--junit-xml=report.xml"])
pytest.main(["-vsx", "TestCalc.py", "-m", "div"])

语法

1
2
3
4
5
6
7
8
9
10
@pytest.mark.smoke
@pytest.mark.get
$pytest -m 'smoke'
  仅运行标记smoke的函数
$pytest -m 'smoke and get'
  运行标记smoke和get的函数
$pytest -m 'smoke or get'
  运行标记smoke或get的函数
$pytest -m 'smoke and not get'
运行标记smoke和标记不是get的函数

分布式测试插件之pytest-xdist的详细使用

前言

  • 平常我们功能测试用例非常多时,比如有1千条用例,假设每个用例执行需要1分钟,如果单个测试人员执行需要1000分钟才能跑完
  • 当项目非常紧急时,会需要协调多个测试资源来把任务分成两部分,于是执行时间缩短一半,如果有10个小伙伴,那么执行时间就会变成十分之一,大大节省了测试时间
  • 为了节省项目测试时间,10个测试同时并行测试,这就是一种分布式场景
  • 同样道理,当我们自动化测试用例非常多的时候, 一条条按顺序执行会非常慢,pytest-xdist的出现就是为了让自动化测试用例可以分布式执行,从而节省自动化测试时间
  • pytest-xdist是属于进程级别的并发

分布式执行用例的设计原则(重中之重的重点)

  • 用例之间是独立的,用例之间没有依赖关系,用例可以完全独立运行【独立运行】
  • 用例执行没有顺序,随机顺序都能正常执行【随机执行】
  • 每个用例都能重复运行,运行结果不会影响其他用例【不影响其他用例】

插件安装

1
pip3 install pytest-xdist -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

pytest-xdist通过一些独特的测试执行模式扩展了pytest

  • 测试运行并行化:如果有多个CPU或主机,则可以将它们用于组合的测试运行。 这样可以加快开发速度或使用远程计算机的特殊资源。
  • --looponfail:在子进程中重复运行测试。 每次运行之后,pytest都会等到项目中的文件更改后再运行之前失败的测试。 重复此过程,直到所有测试通过,然后再次执行完整运行。
  • 跨平台覆盖:您可以指定不同的Python解释程序或不同的平台,并在所有这些平台上并行运行测试。

快速入门

代码的包结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
14xdist是项目文件夹名称
│ conftest.py
│ test_1.py
│ __init__.py

├─test_51job
│ │ conftest.py
│ │ test_case1.py
│ │ __init__.py

├─test_toutiao
│ │ test_case2.py

├─test_weibo
│ │ conftest.py
│ │ test_case3.py
│ │ __init__.py

具体代码

最外层的conftest.py

1
2
3
4
5
6
7
8
9
# 外层conftest.py

@pytest.fixture(scope="session")
def login():
print("====登录功能,返回账号,token===")
name = "testyy"
token = "npoi213bn4"
yield name, token
print("====退出登录!!!====")

最外层的test_1.py

1
2
3
4
5
6
7
8
9
import pytest


@pytest.mark.parametrize("n", list(range(5)))
def test_get_info(login, n):
sleep(1)
name, token = login
print("***基础用例:获取用户个人信息***", n)
print(f"用户名:{name}, token:{token}")

test_51job包下的conftest.py

1
2
3
4
5
6
7
import pytest


@pytest.fixture(scope="module")
def open_51(login):
name, token = login
print(f"###用户 {name} 打开51job网站###")

test_51job包下的test_case1.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from time import sleep

import pytest


@pytest.mark.parametrize("n", list(range(5)))
def test_case2_01(open_51, n):
sleep(1)
print("51job,列出所有职位用例", n)


@pytest.mark.parametrize("n", list(range(5)))
def test_case2_02(open_51, n):
sleep(1)
print("51job,找出所有python岗位", n)

test_toutiao包下的test_case2.py

1
2
3
4
5
6
7
8
9
from time import sleep

import pytest


@pytest.mark.parametrize("n", list(range(5)))
def test_no_fixture(login, n):
sleep(1)
print("==没有__init__测试用例,我进入头条了==", login)

test_weibo包下的conftest.py

1
2
3
4
5
6
7
import pytest


@pytest.fixture(scope="function")
def open_weibo(login):
name, token = login
print(f"&&& 用户 {name} 返回微博首页 &&&")

test_weibo包下的test_case3.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from time import sleep

import pytest


@pytest.mark.parametrize("n", list(range(5)))
class TestWeibo:
def test_case1_01(self, open_weibo, n):
sleep(1)
print("查看微博热搜", n)

def test_case1_02(self, open_weibo, n):
sleep(1)
print("查看微博范冰冰", n)

不使用分布式测试的命令和所需执行时间

1
pytest -s

可以看到,执行一条用例大概1s(因为每个用例都加了 sleep(1) ),一共30条用例,总共运行30s;那么如果有1000条用例,执行时间就真的是1000s

使用分布式测试的命令和所需执行时间

1
pytest -s -n auto

知识点

  • 可以看到,最终运行时间只需要6s,我的电脑是真6核,假12核
  • -n auto:可以自动检测到系统的CPU核数;从测试结果来看,检测到的是逻辑处理器的数量,即假12核
  • 使用auto等于利用了所有CPU来跑用例,此时CPU占用率会特别高

可以指定需要多少个CPU来跑用例

1
pytest -s -n 2

pytest-xdist可以和pytest-html很好的相结合

1
pytest -s -n auto --html=report.html --self-contained-html

pytest-xdist按照一定的顺序执行

pytest-xdist默认是无序执行的,可以通过 --dist 参数来控制顺序

--dist=loadscope

  • 将按照同一个模块module下的函数和同一个测试类class下的方法来分组,然后将每个测试组发给可以执行的worker,确保同一个组的测试用例在同一个进程中执行
  • 目前无法自定义分组,按类class分组优先于按模块module分组

--dist=loadfile

按照同一个文件名来分组,然后将每个测试组发给可以执行的worker,确保同一个组的测试用例在同一个进程中执行

如何让scope=session的fixture在test session中仅仅执行一次

pytest-xdist是让每个worker进程执行属于自己的测试用例集下的所有测试用例

这意味着在不同进程中,不同的测试用例可能会调用同一个scope范围级别较高(例如session)的fixture,该fixture则会被执行多次,这不符合scope=session的预期

如何解决?

虽然pytest-xdist没有内置的支持来确保会话范围的夹具仅执行一次,但是可以通过使用锁定文件进行进程间通信来实现。

小栗子

  1. 下面的示例只需要执行一次login(因为它是只需要执行一次来定义配置选项,等等)
  2. 当第一次请求这个fixture时,则会利用FileLock仅产生一次fixture数据
  3. 当其他进程再次请求这个fixture时,则会从文件中读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pytest
from filelock import FileLock


@pytest.fixture(scope="session")
def login():
print("====登录功能,返回账号,token===")
with FileLock("session.lock"):
name = "testyy"
token = "npoi213bn4"
# web ui自动化
# 声明一个driver,再返回

# 接口自动化
# 发起一个登录请求,将token返回都可以这样写

yield name, token
print("====退出登录!!!====")

pytest分布式执行插件 pytest-xdist 的高级用法

想要使用多个CPU核心来进行测试,可以使用 -n 参数( 或者 --numprocesses)
(使用8个核心来跑测试用例)

1
pytest -n 8

使用 -n auto 参数可以利用电脑的所有核心来跑测试用例
测试时使用的算法可以根据--dist命令参数定制:

  • --dist load(默认选项):给每个CPU核心随机分配用例,不保证执行顺序。
  • --dist loadscope:对于测试函数,测试按模块分组,对于测试方法,测试按类分组。每组作为一个整体分配给可用的worker。这保证了组中的所有测试都在同一进程中运行。如果的模块级或类级fixtures,这将非常有用。按类分组优先于按模块分组。
  • --dist loadfile: 测试用例按其所在文件分组。每组作为一个整体分配给可用的worker。这保证了文件中的所有测试都在同一个辅助进程中运行。
  • --dist loadgroup: 测试按xdist_group标记分组。每组作为一个整体分配给可用的执行器。这保证了具有相同xdist_ group名称的所有测试都在同一个worker中运行。
1
2
3
4
5
6
7
8
@pytest.mark.xdist_group(name="group1")
def test1():
pass

class TestA:
@pytest.mark.xdist_group("group1")
def test2():
pass-

这将确保test1和TestA::test2将在同一个worker中运行。没有xdist_ group标记的测试在--dist=load模式下正常运行。

  • --dist no:正常的pytest执行模式,一次运行一个测试(完全没有分发)

Allure

Allure是一种测试报告生成工具,可以生成漂亮、交互式和易于阅读的测试报告。

Linux环境下allure环境配置

(1)配置jdk(因为allure是基于java编写 )和allure

1
2
3
4
5
6
7
curl -L https://download.java.net/java/GA/jdk11/13/GPL/openjdk-11.0.1_linux-x64_bin.tar.gz -o /usr/local/src/openjdk11.tar.gz \
&& cd /usr/local/src && tar -zxvf openjdk11.tar.gz \
&& rm -f openjdk11.tar.gz \
&& mv jdk* openjdk11 \
&& curl -L https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/2.22.0/allure-commandline-2.22.0.tgz -o /usr/local/src/allure.tgz \
&& tar -xf /usr/local/src/allure.tgz -C /usr/local/ \
&& mv /usr/local/allure* /usr/local/allure

(2)验证allure是否配置成功

1
allure --version

Win环境下allure环境配置

allure下载

1
2
3
4
5
6
7
下载路径一:
在github上下载:https://github.com/allure-framework/allure2/releases
解压到指定文件夹

下载路径二:
在官网下载:https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/
Windows选择一个版本,并选择下载zip文件

环境变量配置

(1)下载完后直接解压到某路径下,在环境变量path中添加allure路径

(2)验证allure是否配置成功

1
allure --version

代码样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if __name__ == '__main__':
# 执行mark
tag = 'P3'
formatted_time = datetime.now().strftime('%Y%m%d_%H_%M_%S')
# 执行main文件
pytest.main(["-v", "-m", tag, "--alluredir=Outputs/allure_report"])
# 生成allure报告
if platform.system() == "Windows":
command_1 = "cd {}".format(FileConfig().get_path(type="allure_report"))
command_2 = "allure generate {0} -o {1} --clean".format(FileConfig().get_path(type="allure_report"),
formatted_time)
command_3 = "for %i in (*.json *.txt) do if exist %i del /f /q \"%i\""
os.system('{0}&&{1}&&{2}'.format(command_1, command_2, command_3))
else:
command_1 = "cd {}".format(FileConfig().get_path(type="allure_report"))
command_2 = "allure generate {0} -o {1} --clean".format(FileConfig().get_path(type="allure_report"),
formatted_time)
command_3 = "find . -maxdepth 1 -type f \( -name '*.json' -o -name '*.txt' \) -delete"
os.system('{0}&&{1}&&{2}'.format(command_1, command_2, command_3))