Pytest测试框架
pytest介绍
- pytest是一个非常成熟的全功能的python测试框架
- 简单灵活,易上手
- 可以参数化
- 可支持简单的单元测试和负载的功能测试,还可以与selenium/appnium做自动化测试,接口测试可与request结合
- 支持与jenkins集成 devops
- 特殊用法:skip和xfail,自动失败处理机制
样例
def fun(x):
return x + 1
def test_answer():
assert fun(3) ==5
黑窗口执行
pytest安装方式以及第三方库结合使用
- pip install -U pytest U升级pytest
- pip install pytest-sugar 美化管理台的数据
- pip install pytest-rerunfailures 测试案例失败后重新尝试执行
- pip install pytest-xdist 分布式执行和多线程执行
- pip install pytest-assume 测试案例有多条断言,一条失败不影响下一条的断言
- pip install pytest-html 生成测试报告
测试用例的识别与运行
- 测试文件
- test_*.py
- *_test.py
- 用例识别
- Test*类包含的所有test_*的方法(测试类不能带有__init__方法)
- 不在class中的所有test_*方法
- pytest也可以执行unittest框架写的用例和方法
终端执行的常用参数
- pytest /py.test
- pytest -v 打印详细运行日志信息
- pytest -v -s 文件名 打印日志信息以及测试案例中管理台输出语句的内容
- pytest 文件名.py 执行单独一个pytest模块
- pytest 文件名.py::类名 运行某个文件中的某个类
- pytest 文件名.py::类名::方法名 运行某个文件中某个类的某个方法
- pytest -v -k ‘类名 and not 方法名’ 跳过运行某个用例
- pytest -m [标记名] 运行pytest.mark[标价名]的测试用例
- pytest -x 文件名 一旦运行出错停止运行
- pytest --maxfail=[num] 当运行中出现了num的失败的案例书就停止运行
测试代码
def test_one():
print("开始执行 test_one用法")
a = 'this'
assert 'h' in a
def test_two():
print("开始执行 test_two放啊")
x = 'hello'
assert 'e' in x
def test_three():
print("开始执行 test_three方法")
a = 'hello'
b = 'hello world'
assert a in b
###运行方式一
运行方式二
运行方式三
###测试代码二
class TestDemo():
def test_one(self):
print("开始执行 test_one用法")
a = 'this'
assert 'h' in a
def test_two(self):
print("开始执行 test_two放啊")
x = 'hello'
assert 'e' in x
def test_three(self):
print("开始执行 test_three方法")
a = 'hello'
b = 'hello world'
assert a not in b
class TestDemo1():
def test_a(self):
print("开始执行 test_a用法")
a = 'this'
assert 'h' in a
def test_b(self):
print("开始执行 test_b放啊")
x = 'hello'
assert 'e' in x
def test_c(self):
print("开始执行 test_c方法")
a = 'hello'
b = 'hello world'
assert a in b
执行方式一
执行方式二
执行方式四
执行方式五
执行方式六
pytest执行失败-重新运行
- 场景
- 测试失败后要重新运行n次,要在重新运行之前添加延迟时间,间隔n秒在运行
- 安装:
- pip install pytest-rerunfailures
- 执行
- pytest -reruns 3 -v -s xxx.py
- pytest -v --reruns 5 --reruns-delay 1
- 一定要用命令行安装插件,会减少丢包的操作
pytest执行-多条断言有失败也都运行
- 场景:一个方法中写多天断言,如果第一条断言失败,会导致方法发下的其它断言都无法执行
- 安装
- pip install pytest-assume
- 执行:
- pytest.assume(1==4)
- pytest.assume(2==4)
安装
测试代码
import pytest
class TestDemo():
def test_one(self):
print("开始执行 test_one用法")
a = 'this'
assert 'h' in a
pytest.assume(1 == 2)
pytest.assume(1 == 3)
def test_two(self):
print("开始执行 test_two放啊")
x = 'hello'
assert 'e' in x
def test_three(self):
print("开始执行 test_three方法")
a = 'hello'
b = 'hello world'
assert a not in b
class TestDemo1():
def test_a(self):
print("开始执行 test_a用法")
a = 'this'
assert 'h' in a
def test_b(self):
print("开始执行 test_b放啊")
x = 'hello'
assert 'e' in x
def test_c(self):
print("开始执行 test_c方法")
a = 'hello'
b = 'hello world'
assert a in b
if __name__ == '__main__':
pytest.main()
执行方式+结果
测试用例的识别与运行(IDE工具中)
- pycharm配置与执行pytest测试框架
- 运行方式:
- pytest.main([‘-v’,‘测试类’]) (传的参数与pytest命令行方式是一样的)
pytest框架结构
- import pytest 类似的setup,teardown同样更灵活
- 模块级(setup_module/teardown_module)模块始末,全局的(优先级最高)
- 函数级(setup_function/teardown_function)只对函数用例生效(不在类中叫函数)
- 类级(setup_class/teardown_class)指在类中前后运行一次
- 方法级(setup_method/teardown_method)开始于方法始末
- 类里面的(setup/teardown)运行在调用方法的前后
实战代码
import pytest
def setup_module():
print("这个是setup_module方法")
def teardown_module():
print("这个是teardown_module方法")
def setup_function():
print("这个是setup_function方法")
def teardown_function():
print("这个是teardown_function方法")
def test_login():
print("这是一个外部的方法")
class TestDemo():
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_one(self):
print("开始执行 test_one用法")
a = 'this'
assert 'h' in a
# pytest.assume(1 == 2)
# pytest.assume(1 == 3)
def test_two(self):
print("开始执行 test_two放啊")
x = 'hello'
assert 'e' in x
def test_three(self):
print("开始执行 test_three方法")
a = 'hello'
b = 'hello world'
assert a not in b
# if __name__ == '__main__':
# pytest.main()
运行后展示
pytest-fixture的用法
- 场景:
- 用例一需要先登录
- 用例二不需要登录
- 用例三需要登录
- 这种场景无法用setup与teardown实现
- 用法
- 在方法前面加@pytest.fixture()
应用场景:前端自动化中应用
- 场景:测试用例执行时,有的用例需要登陆才能执行,有些不需要登录。setup和teardown无法满足。fixture可以,默认scope(范围)function
- 实现步骤
- 导入pytest
- 在登陆的函数上加@pytest.fixture()
- 在要使用的测试方法中传入(登录函数名称)就先登陆
- 不传入的就不登陆直接执行测试方法
实战
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
@pytest.fixture()
def login():
print("这是个登录方法")
def test_case1(login):
print("test_case1")
def test_case12(login):
print("test_case2")
def test_case3(login):
print("test_case3")
if __name__ == '__main__':
pytest.main()
前端自动化中应用-conftest
- 场景:
- 协同开发的时候,公共要在不同文件中,要在大家都访问到的地方
- 解决
- conftest.py这个文件进行数据共享,并且可以放在不同位置起着不同的范围共享作用
- 执行:
- 系统执行到参数login时先从本地文件中查找是否有这个名字的变量,之后再conftest.py中找是否存在
- 步骤:
- 将登陆模块的函数上加@pytest.fixture()写在conftest.py中
- conftest文件配置注意事项
- 首先文件名必须是conftest
- conftest.py与运行的用例在同一个package下,并且有__init__.py文件
- 不需要导入conftest.py,pytest用例会自动查找的
- 全局的配置和前期工作都可以写在这里,放在具体某个包下,就是这个包的数据共享的地方
实战
#!/usr/bin/env/ python
# -*- coding: utf-8 -*-
import pytest
# 不带参数的fixture默认参数为 scope=function
@pytest.fixture()
def login():
print("这是登陆方法")
def pytest_configure(config):
mark_list = ["search", "login"] # 标签名集合
for markers in mark_list:
config.addinivalue_line(
"markers", markers
)
前端自动化中应用-yield
- 场景:通过conftest已经可以将用例执行前的依赖解决,但是执行后的销毁清除数据要如何进行?范围是模块级别的。类似setupclass
- 解决:
- 通过在同一模块中加入yield关键字,yield时调用第一次返回结果,第二次执行它下面的语句返回
- 步骤:
- 在@pytest.fixture(scope=module)
- 在登陆的方法中加yield,之后加销毁清除的步骤需要注意,这种方式没有返回值,如有希望返回值需要使用addfinalizer
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
# 作用域:module是在模块之前执行,模块之后执行
@pytest.fixture(scope="module")
def open():
print("打开浏览器")
yield
print("执行teardown!")
print("最后关闭浏览器")
def test_search1(open):
print("test_case1")
raise NameError
def test_search2(open):
print("test_case2")
# raise NameError
def test_search3(open):
print("test_case3")
# raise NameError
if __name__ == '__main__':
pytest.main()
运行后代码截图
fixture的自动应用
- 场景
- 不想原测试方法有任何改动,或全部都自动实现自动应用,没特例,也都不需要返回值时可以选择自动应用
- 解决:
- 使用fixture中参数autouse=True
- 步骤:
- 在方法上加@pytest.fixture(autouse=True)
- 在测试方法上加@pytest.mark.usefixtures(“start”)
代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
@pytest.fixture(autouse=True)
def open():
print("打开浏览器")
print("执行teardown!")
print("最后关闭浏览器")
def test_search1():
print("test_case1")
raise NameError
def test_search2():
print("test_case2")
# raise NameError
def test_search3():
print("test_case3")
# raise NameError
if __name__ == '__main__':
pytest.main()
执行后代码
fixture带参数传递
- 场景:测试离不开数据,为了数据灵活,一般数据都是通过参数传递的
- 解决:fixture通过固定参数request传递
- 步骤:
- 在fixture中增加@pytest.fixture(params=[1,2,3,‘linda’])在方法参数写request
代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
@pytest.fixture(params=[1, 2, 3, 'linda'])
def test_data(request):
return request.param
def test_one(test_data):
print('\ntest data: %s ' % test_data)
执行后代
进阶代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import pytest
# 参数化 前两个变量,后面是对应的数据
# 3+5 -> test_input 8->expect
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+5", 7), ("7+5", 30)])
def test_eval(test_input, expected):
# eval 将字符串str当成有效的表达式求值,并返回结果
assert eval(test_input) == expected
# 参数组合
@pytest.mark.parametrize("y", [8, 10, 11])
@pytest.mark.parametrize("x", [1, 2])
def test_foo(x, y):
print(f"测试数据组合x:{x},y:{y}")
# 方法名作为参数
test_user_data = ['Tome', 'jerry']
@pytest.fixture(scope='module')
def login_r(request):
# 这是接受并传入的参数
user = request.param
print(f"\n打开首页准备登录,登录用户:{user}")
return user
# indirect=True 可以把传过来的参数当函数执行
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值{a}")
assert a != ""
# indirect=True 可以把传过来的参数当函数执行
@pytest.mark.skip("此次测试不执行登录")
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值{a}")
assert a != ""
# sys.platform == "win32" 获取当前系统型号
@pytest.mark.skipif(sys.platform == "win32", reason="不在win上执行")
# indirect=True 可以把传过来的参数当函数执行
# @pytest.mark.skip("此次测试不执行登录")
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值{a}")
assert a != ""
# 当前案例不执行直接给xpass
@pytest.mark.xfail
# indirect=True 可以把传过来的参数当函数执行
# @pytest.mark.skip("此次测试不执行登录")
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值{a}")
assert a != ""
# 当前案例不执行直接给xfail
@pytest.mark.xfail
# indirect=True 可以把传过来的参数当函数执行
# @pytest.mark.skip("此次测试不执行登录")
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值{a}")
assert a != ""
raise NameError
mark中的skip与xfail
- Skip使用场景
- 测试时不想运行这个用例
- 标记无法在某平台上运行的测试功能
- 在某些版本上执行,其他版本中跳过
- 当前的外部资源不可用时跳过(如数据库不可用的时候)
- 解决:
- @pytest.mark.skip(跳过这个测试用例,或者可以加skipif,在满足条件下才希望执行)
- xfail场景
- 功能测试尚未实现修复的缺陷,当测试通过时尽管会失败(@pytest.mark.xfail),他是一个xpass,将在测试报告中显示摘要
- 你希望测试由于某种原因就应该失败 加一个 raise 错误类型
- 解决:
- @pytesr.mark.xfail
代码在上面的一张图片中
使用自定义标记mark只执行某部份用例
- 场景:
- 只执行符合要求的某一部分用例,可以把一个web项目分多个模块,然后指定模块去执行
- App自动化时,如果想Android和OS共用一部分代码时,也可以使用标记功能,标明哪些时OS的用例,哪些是adnroid的运行代码时指定mark名称运行
- 解决:
- 在测试用例方法上加@pytest.mark.webtest(webtest这个可自定义)
- 执行:
- -s参数:输出所有测试用例的print信息
- -m:执行自定义标记的相关用例
- pytest -s test.py -m=webtest
- pytest -s test.py -m=apptest
- pytest -s test.py -m “not ios”
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
@pytest.mark.search
def test_search1():
print("test_search1")
raise NameError
@pytest.mark.search
def test_search2():
print("test_search2")
@pytest.mark.search
def test_search3():
print("test_search3")
@pytest.mark.login
def test_login1():
print("test_login1")
@pytest.mark.login
def test_login2():
print("test_login2")
@pytest.mark.login
def test_login3():
print("test_login3")
if __name__ == '__main__':
pytest.main()
执行后命令
标记执行案例提示waning警告解决办法
#!/usr/bin/env/ python
# -*- coding: utf-8 -*-
import pytest
# 不带参数的fixture默认参数为 scope=function
@pytest.fixture()
def login():
print("这是登陆方法")
# 解决warn报错的方法,将标记的后缀加入以下列表中即可,固定写法
def pytest_configure(config):
mark_list = ["search", "login"] # 标签名集合
for markers in mark_list:
config.addinivalue_line(
"markers", markers
)
以上代码要放到confftest文件中才可生效
多线程并行与分布式执行
场景:
- 测试用例1000条,一个用例执行1分钟,一个测试人员需要1000分钟,这样效率太低
解决:
pytest分布式执行插件:pytest-xdist多个CPU或主机运行
前提案例之间无依赖
安装:
- pip3 install pytest-xdist
- 多个CPU并行执行用例,直接加-n 3是并行数量:pytest -n 3
- 在多个终端下一起执行
执行后代码
pytest-html生成报告
- 安装:pip install pytest-html
- 生成html报告:pytest -v -s --html=report.html --self-contained-html
执行代码后
打开这个文件即可
pytest数据参数化
- 使用方法
@pytest.mark.parametrize(argnames,argualues)
- argnames:要参数化的变量,string(逗号分隔),list,tuple
- argvalues:参数化的值,list,list[tuple]
使用string
@pytest.mark.parametrize("a,b",[(10,20),(10,30)])
def test_param(a,b): print(a+b)
使用list
@pytest.mark.paramterize(["a","b"],{(10,20),(10,30)})
def test_param(a,b): print(a+b)
使用tuple
@pytest.mark.paramterize(("a","b"),[(10,20),(20,30)])
def
test_param(a,b): print(a+b)
代码练习
import pytest
# 使用字符串
class TestData:
@pytest.mark.parametrize("a,b", [(1, 2), (2, 3), (3, 4)])
def test_data(self, a, b):
print(a + b)
# 使用元组
class TestData1:
@pytest.mark.parametrize(("a", "b"), [(1, 2), (2, 3), (3, 4)])
def test_data(self, a, b):
print(a + b)
# 使用列表
class TestData2:
@pytest.mark.parametrize(["a", "b"], [(1, 2), (2, 3), (3, 4)])
def test_data(self, a, b):
print(a + b)
执行后截图
yaml基本使用
yaml实现list
- -list
- -10
- -20
- -30
yaml实现字典
- dict
- by:id
- locator:name
- action:click
yaml进行嵌套1
- -
- -by:id
- -locator:name
- -actiong:click
yaml进行嵌套2
- companies:
- -
- id:1
- name:company1
- price:200$
- -
- id:2
- name:company2
- price:500$
格式:companies:[{id:1,name:company1,price:200$},{}]
加载yaml文件
yanl.safe_load(open("./data.yaml"))
加载yanl
@pytest.mark.paramterize(["a","b"],yaml.safe(open("./data.yaml")))
def test_param(a,b): print(a+b)
代码练习
import pytest
import yaml
# 使用yaml
class TestData:
@pytest.mark.parametrize("a,b", yaml.safe_load(open("./data.yaml")))
def test_data(self, a, b):
print(a + b)
-
- 10
- 20
-
- 10
- 20
python数据驱动
数据驱动简介
数据驱动是数据的改变从而驱动自动化测试的执行;最终引起结果的改变。
应用场景
- App/Web/接口自动化测试
- 测试步骤的数据驱动
- 测试数据的数据驱动
- 配置的数据驱动
###数据驱动案例
import pytest
import yaml
class TestDemo:
@pytest.mark.parametrize("env",yaml.safe_load(open("./env.yaml")))
def test_demo(self, env):
print(env)
if "test" in env:
print(f"测试环境地址{env['test']}")
elif "dev" in env:
print("开发环境")
if __name__ == '__main__':
pytest.main(["-v","-s"])
-
test: 127.0.0.1
Allure测试框架
allure介绍
- allure是一个轻量级、灵活的,支持多语言的测试报告工具
- 多平台的奢华的report框架
- 可以为dev/qa提供详尽的测试报告,测试步骤、log
- 为管理层提供high level统计报告
- Java语言开发的,支持pytest,Javascript,php,ruby等
- 可以集成到Jenkins
allure安装
国内下载地址:https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/
配置环境变量
pytest-allure插件
Allure报告的生成
- 安装allure-pytest插件
- pip install allure-pytest
- 运行
- 在测试执行期间收集结果
- pytest [测试文件] -s -q -alluredir=./result(–alluredir这个选项用于存储测试结果的路径)
- 查看测试报告
- 方式一:测试完成后查看实际报告,在线看报告,会直接打开默认浏览器的展示当前的报告
- allure serve ./result/ 踩坑:serve的书写
- 方式二:从结果生成报告,这是一个启动tomcat的服务,需要两个步骤,生成报告,打开报告
- 生成报告
- allure generate ./result/ -o ./report/ --clean(注意:覆盖路径加–clean)
- 打开报告
- allure open -h 127.0.0.1 -p 8889 ./report/
实战代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
def test_success():
"""this test success"""
assert True
def test_failure():
"""this test fail"""
assert False
def test_skip():
"""this test is skipped"""
pytest.skip("for a reason")
def test_broken():
raise Exception('oops')
执行方式一
执行方式二
allure特性分析
- 场景:
- 希望在报告中看到测试功能,子功能或场景,测试步骤,包括测试附加信息
- 解决:
- @Feature,@story,@step,@attach
- 步骤:
- import allure
- 功能上加@allure.feature(“功能名称”)
- 子功能上加@allure.story(“子功能名称”)
- 步骤上加@allure.step(“步骤名称”)
- @allure.attach(“具体文本信息”),需要附加的信息,可以是数据、文本、图片、视频、、网页
- 如果只测试登录功能运行的时候可以限制过滤
- pytest 文件名 --allure-features “购物车功能” --allure-stories “加入购物车” (–allure_feature中间是下划线)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
import allure
@allure.feature("登录模块")
class TestLogin():
@allure.story("登录成功")
def test_login_success(self):
print("这是登录:测试用例,登陆成功")
@allure.story("登录失败")
def test_login_success_a(self):
print("这是登录:测试用例,登登录成功")
@allure.story("用户名缺失")
def test_login_success_b(self):
print("用户名缺失")
@allure.story("密码缺失")
def test_login_failure(self):
with allure.step("点击用户名"):
print("输入用户名")
with allure.step("点击密码"):
print("输入密码")
print("点击登录")
with allure.step("点击登录之后登陆失败"):
assert '1' == 1
print("登录失败")
@allure.story("登录失败")
def test_login_failure_a(self):
print("这是登录:测试用例,登陆失败")
报告生成流程
allure特性-feature/story
- 注解@allure.feature与@allure.store的关系
- feture相当于一个功能,一个大的模块,将case分类到某个feature中显示,相当于behaviore中显示,相当于testcase
- story相当于对应这个功能或者模块下的不同场景,分支功能,属于feature之下的结构,报告在features中显示,相当于testcase
- feature与story蕾西与父子关系
按feature/story运行
allure特性-step
- 测试过程中每个步骤,一般放在其具体逻辑方法中
- 可以放在实现步骤中,在报告中显示
- 在app/web自动测试中,建议每切换到一个新的页面当作一个step
- 用法:
- @allure.skip()只能以装饰器的形式放在类或方法上面
- with allure.step():可以放在测试用例方法里面,但测试步骤的代码需要被该语句包含
allure特性-issue,testcase
- 关联测试用例(可以直接给测试用例的地址链接)
- 关联bug
- 执行的时候需要加上个参数
- –allure-link-pattern=issue:具体地址
代码
@allure.issue('140','')
def test_with_link():
pass
Test_case_link="具体地址" @allure.testcase(Test_case_link,"Test case title") def test_with_testcase_link(): pass
代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import allure
@allure.link("http://www.baidu.com", name="百度链接")
def test_with_link():
print("这是一个加了链接的测试")
TEST_CASE_LINK = "http:...."
@allure.testcase(TEST_CASE_LINK, "登录用例")
def test_with_testcase_link():
print("这是一条测试用例的链接,链接到测试用例")
# --allure-link-pattern=issue:http://www.mytesttracker.com/issue/{}
@allure.issue('140', '这是一个issue')
def test_with_issue_link():
pass
运行方式
运行后截图
按重要性级别进行一定范围的测试
- 场景:
- 通常测试有Po、冒烟测试,验证上线测试,按重要性级别分别执行的,比如上线要把主流程和重要模块都跑一遍
- 解决:
- 通过附加pytest.mark标记
- 通过allure.feature,allure.story
- 也可以通过allure.severity来附加标记
- 几倍Trivial不重要,Minor不太重要,Normal正常问题,Critical严重,Blocker:阻塞
- 步骤:
- 在方法函数和类上面加
- @pytest.severity(allure.severity_level.TRIVIAL)
- 执行时
- pytest -s -v 文件名 --allure-severities normal,critical
代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
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
执行后截图
通过IDE执行
前端自动化测试截图
- 场景:
- 前端自动化测试经常需要附加图片或html,在适当的低昂,适当的实际截图
- 解决:
- @allure.attach显示许多不同类型的提供的附件,可以补充测试,步骤或测试结果
- 步骤:
- 在测试报告中附加网页:
- allure.attach(body(内容),name,attachment_type,extension)
- allure.attach(‘html语言’,‘这是错误信息’,allure.attachment_type.HTML)
- 在测试报告中附加图片
- allure.attach.file(source,name,attachment_type,extension)
- allure.attach.file(‘,/result/photo.png’,attachment_type=allure.attachment_type.PNG)
代码
```PYTHON
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import allure
def test_attach_text():
allure.attach("这是一个纯文本", attachment_type=allure.attachment_type.TEXT)
def test_attach_html():
allure.attach("""<body>这是一个htmlbody模块</body>)
""", "html模块", allure.attachment_type.HTML)
def test_attach_photo():
allure.attach.file("C:/Users/Administrator/Desktop/pytesy框架/1.png", name="截图",
attachment_type=allure.attachment_type.PNG)
# allure.description("这条测试用例是用来说明allure的description)allure-pytest+selenium实战演示
```
##### 执行后截图
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CLocal%5CTemp%5C1724632110704.png&pos_id=img-aAuRj34g-1725267687491)
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CLocal%5CTemp%5C1724632075765.png&pos_id=img-FAq8avPr-1725267687522)
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CLocal%5CTemp%5C1724632088071.png&pos_id=img-SGOBZBZs-1725267687524)
#### 实战
```python
#!/bin/env/ python
# -*- coding: utf-8 -*-
import allure
import pytest
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
@allure.feature('百度搜索')
@pytest.mark.parametrize('test_detail', ['allure', 'pytest', 'unittest'])
def test_steps_demo(test_detail):
with allure.step("打开百度网页"):
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
driver.maximize_window()
with allure.step(f"输入搜索词{test_detail}"):
driver.find_element(By.ID, "kw").send_keys(test_detail)
time.sleep(2)
driver.find_element(By.ID, 'su').click()
time.sleep(2)
with allure.step("保存图片"):
driver.save_screenshot("./result/baidu/b.png")
allure.attach.file("./result/baidu/b.png", attachment_type=allure.attachment_type.PNG)
allure.attach('<head></head><body>首页</body>', 'attach with HTML type', allure.attachment_type.HTML)
with allure.step("关闭浏览器"):
driver.quit()
```
##### 执行
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CLocal%5CTemp%5C1724634108792.png&pos_id=img-a3JIrJhg-1725267687526)
cription("这条测试用例是用来说明allure的description)allure-pytest+selenium实战演示
```
##### 执行后截图
[外链图片转存中...(img-aAuRj34g-1725267687491)]
[外链图片转存中...(img-FAq8avPr-1725267687522)]
[外链图片转存中...(img-SGOBZBZs-1725267687524)]
#### 实战
```python
#!/bin/env/ python
# -*- coding: utf-8 -*-
import allure
import pytest
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
@allure.feature('百度搜索')
@pytest.mark.parametrize('test_detail', ['allure', 'pytest', 'unittest'])
def test_steps_demo(test_detail):
with allure.step("打开百度网页"):
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
driver.maximize_window()
with allure.step(f"输入搜索词{test_detail}"):
driver.find_element(By.ID, "kw").send_keys(test_detail)
time.sleep(2)
driver.find_element(By.ID, 'su').click()
time.sleep(2)
with allure.step("保存图片"):
driver.save_screenshot("./result/baidu/b.png")
allure.attach.file("./result/baidu/b.png", attachment_type=allure.attachment_type.PNG)
allure.attach('<head></head><body>首页</body>', 'attach with HTML type', allure.attachment_type.HTML)
with allure.step("关闭浏览器"):
driver.quit()
```
##### 执行
[外链图片转存中...(img-a3JIrJhg-1725267687526)]
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CLocal%5CTemp%5C1724634130338.png&pos_id=img-EhpEceOT-1725267687528)