前言
简单灵活,容易上手;支持参数化; 测试用例的skip和xfail处理;
能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytest+requests);
pytest具有很多第三方插件,并且可以自定义扩展, 比较好用的如 pytest-allure(完美html测试报告生成) pytest-xdist(多CPU分发)等;
可以很好的和jenkins集成;
如何运行
1.pytest将在当前目录及其子目录中运行test _ * .py或* test.py形式的所有文件。
2.以test_开头的函数,以Test开头的类,以test_开头的方法。所有包package都要有__init_.py文件。
3.Pytest可以执行unittest框架写的用例和方法
本期内容
1.安装并运行pytest
2.了解pytest灵活框架结构和灵活执行方式
3.断言
4.Pytest-fixture 是最闪亮功能!!
5.yield与final
正在努力
一.安装pytest
打开pycharm 打开终端 输入 pip install pytest 来安装
没有 自定义版本 他会安装最新版本
运行
cd 进入你要执行文件的目录
1.可以多个执行
直接终端CD进入目录 输入pytest
就可运行 全部的test测试文件
可以看到 我是再unittest目录下执行 我要执行的文件
并且可以看到已经全部执行了
图中绿色的点 代表 执行成功 黄色的S代表 跳过
也可以单个执行
直接终端CD进入目录 输入pytest+文件名 执行
可以看到 只有一个文件被执行
pytest+ -v+文件名 执行 可以看到 执行的信息 错误和正确的测试信息
pytest+ -s+文件名 执行 可以看到测试的输出
二.pytest灵活框架结构和灵活执行方式
———灵活架构
写一个简单的架构
1.先更改一下执行工具
2.打开pycharm 新建一个文件 名字为 test_pytest_demo(pytest执行的文件都要命名为test)
写入架构
import pytest
def setup_module():
print("一个文件开始只执行一次")
def setup_function():
print("每一个函数前执行一次")
def test_web_function01():
print("函数级不在类中web的方法")
def test_app_function01():
print("函数级不在类中app的方法")
def teardown_function():
print("每一个函数后执行一次")
def teardown_module():
print("一个文件结尾只执行一次")
class TestPytest(object):
def setup_class(self):
print("一个类 初始化只执行一次")
def setup_method(self):
print("每一个方法前执行一次")
def test_method01_win(self):
print("在win下执行测试")
def test_method02_linux(self):
print("在linux下执行测试")
def teardown_method(self):
print("每一个方法后执行一次")
def teardown_class(self):
print("一个类销毁只执行一次")
if __name__ == '__main__':
pytest.main()
右键运行这个架构 记住要类级别执行
这样 就可以看到测试成功了 pytest执行框架 是最好的
可以看到详情 简易执行
跳过
@pytest.mark.skip(“看你不顺眼 就跳过”)
直接跳过 不会执行 并且不计入失败测试
括号里为 你写的条件 这个要写在方法前
@pytest.mark.xfail()
将注定会失败的执行标记为失败 报告里 会标记为通过
可以看到 我写了个错误的断言 但是他标记为忽略 实则是错误的执行
但是并没有记入失败
标记
pytest -v -s -m apptest 执行用例是使用 @pytest.mark.apptest 标记的测试方法。
pytest -v -s -m “not apptest” 执行用例没有 @pytest.mark.apptest 标记的测试方法。
pytest -v -s -m linuxtest 执行用例是使用 @pytest.mark.linuxtest 标记的测试方法。
pytest -v -s -m “not linuxtest” 执行用例没有 @pytest.mark.linuxtest 标记的测试方法。
selected(被选择的,pytest -m linuxtest )1 selected .只有一 个 被选择上了。5 deselected 5个没有被 选择执行上。
———灵活执行方式
第一种方法 也就是 pycharm终端运行的方式
第二种方法 也就是cmd 运行
-S 可以输出每一个方法执行的结果
-v 显示信息强度 也就是 结果里面 每一步的执行过程
可以 - v -v
也可以 -v -v -v 最高强度三个-v
第三种 不加装饰器标记也想执行不同层次的用例
pytest -v -s test_pytest_frame.py::TestPytest (TestPytest这个类下面的所有方法)
第四种pytest -k "TestPytest or test_app_function02
• pytest -k "TestPytest or test_app_function02"
执行TestPytest这个类下面的所有方法+ test_app_function02方法
三.简单介下断言
断言的使用
• 断言:支持显示最常见的子表达式的值,包括调用,属性,比较以及二元和一元运算符。
• 包含,相等,不等,大于 小于运算,assertnot 假
• assert “h” in “hello”(判断h在hello中)
• assert 3==4(判断3=4)
• assert 3!=4(判断3!=4)
• assert f() ==4 (判断f()方法返回值是否=4)
• assert 5>6 (判断5>6为真)
• assert not xx (判断xx不为真)
• assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
四.Pytest -fixture 是最闪亮功能!!
• 场景1:测试用例执行时,有的用例的数据保存在数据库中,需要先连接数据库再执行测试用例,最后停止数据库的连接。setup和teardown可以实现。fixture可以灵活命名实现。默认scope(范围)function
• 步骤:
- 导入pytest
- 在登陆的函数上面加@pytest.fixture()
- 在要使用的测试方法中传入(登陆函数名称),就先登陆
- 不传入的就不登陆直接执行测试方法。
• 场景2: • 不想原测试方法有任何改动,或全部都自动实现自动应用,没特例,也都不需要返回值时可以选择自动应用
• 解决: • 使用fixture中参数autouse=True实现
• 步骤:在方法上面加@pytest.fixture(autouse=True)
自动应用 示例如下
• 使用@pytest.mark.usefixtures
• 步骤:在测试方法上加@pytest.mark.usefixtures(“方法名")
通过装饰器 示例如下
场景3 执行前 准备测试数据,通过fixture准备
import pytest
@pytest.fixture()
def data():
test_data = {'name': 'Guo', 'age': 18}
return test_data
@pytest.mark.usefixtures("data")
def test_pay(data):
name = data['name']
age = data['age']
print(name + "支付", "今年:", age)
@pytest.mark.usefixtures("data")
def test_basket():
print(data)
print("添加购物车")
def test_search():
print("搜索商品,不需要登陆")
五.yield与final
定义:yield , 调用一次,给你一个反馈,调用第二 次,反馈从第一次结束语句开始给你反馈
神奇的yield
测试用例如下:
pytest运行顺序
1先运行16行的
2在16行找到的参数open_close_broswer
3找到这个参数再12行
4返回这个参数执行并带入行
5执行yield
6执行17行
717行调用12行的参数
8执行关闭浏览器
使用这 个结构,+selenium,实现bing_sous效果
1.打开浏览器是两个测试方法依赖,要在初 始 化实现。 使用yield 返回driver 。
#在一个 方法实现打开关闭浏览器,并且是初始化销毁
import time
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.fixture()
def open_close_broswer():
# 1. 使用浏览器驱动启动浏览器(chrome)
driver = webdriver.Chrome("E:\selenium_project\driver\chromedriver.exe")
# 输入要测试的网址
driver.get("https://cn.bing.com/")
yield driver
# 关闭浏览器,释放资源
driver.close()
def test_soso(open_close_broswer):
# 通过ID搜索
search_elementl = open_close_broswer.find_element(By.ID, "sb_form_q")
# 清楚搜索框中的内容
search_elementl.clear()
# 输入搜索信息
search_elementl.send_keys("selenium")
# 通过NAME定位这个搜索框
# search_elementl = driver.find_element(By.NAME,"q")
# 定位到[搜索按钮]进行点击
open_close_broswer.find_element(By.ID, "search_icon").click()
# 断言(assert )操作, 验证搜索的信息在网页返回源码中
assert "selenium" in open_close_broswer.page_source
time.sleep(2)
def test_picture(open_close_broswer):
print("点击图片,搜索图片")
运行后新的问题出现了
你发现打开两次浏览器 ,能 否只 打开一次 ,需要类级文件级初始化,不是 方法级的。
可以的 只需要加一个@pytest.fixture(scope="module")
让他只执行一次
后面在调用 他不执行
• 场景1:
• 测试离不开数据,为了数据灵活一般,数据都是通过参数传的
• 解决:fixture通过固定参数request传递;
• 步骤: • 在fixture中增加@pytest.fixture(params=[1, 2, 3, ‘linda’]) • 在方法参数写request
可以看到 执行了三次
做两个小任务
任务一:把加法数据从文件中传。尝试
fixture----初 始化 依赖 -unittest–setup pytest.mark.paramilize—参数化数据 ddt
1.新建一个yaml 文件test_add_data.yaml 写入列表
这里要记住yaml文件格式-空格XXX就行
可以新建一个python文件 看看能不能把他读出来
是可以读出来的 encoding='utf8’代表 中文字符输出
接下来进行下一步使用参数化 输出
@pytest.mark.parametrize("num1,num2,result",yaml.safe_load(open("test_add_data.yaml",encoding='utf8')))
def test_add_yaml(num1, num2, result):
assert num1 + num2 == result
输出成功拉
任务二:将bing搜索改成参数化数据驱动(pytest–,yaml)
import time
import pytest
import yaml
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.fixture(scope="module")
def open_close_broswer():
# 1. 使用浏览器驱动启动浏览器(chrome)
driver = webdriver.Chrome("E:\selenium_project\driver\chromedriver.exe")
# 输入要测试的网址
driver.get("https://cn.bing.com/")
yield driver
# 关闭浏览器,释放资源
driver.close()
@pytest.mark.parametrize("a", yaml.safe_load(open("test_data.yaml", encoding='utf8')))
def test_soso(open_close_broswer,a):
open_close_broswer.get("https://cn.bing.com/")
time.sleep(2)
# 通过ID搜索
search_elementl = open_close_broswer.find_element(By.ID, "sb_form_q")
# 清楚搜索框中的内容
search_elementl.clear()
# 输入搜索信息
search_elementl.send_keys(a)
# 通过NAME定位这个搜索框
#search_elementl = driver.find_element(By.NAME,"q")
# 定位到[搜索按钮]进行点击
open_close_broswer.find_element(By.ID, "search_icon").click()
# 断言(assert )操作, 验证搜索的信息在网页返回源码中
assert "a" in open_close_broswer.page_source
time.sleep(2)
def test_picture(open_close_broswer):
print("点击图片,搜索图片")
driver.get(“https://cn.bing.com/”)
加这个是因为 你每次搜索完是新的网页 你需要回到原来的网页 重新搜索
也可以把上面的浏览器驱动
driver = webdriver.Chrome("E:\selenium_project\driver\chromedriver.exe")
换成这个
#..替换的是当前路径 也就是上一级路径
par_path = os.path.abspath('..')
driver_path = par_path + r"\driver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
yaml 大家随便写 只要记住格式为
-空格XXX
即可
多线程并行与分布式执行
多线程并发:考虑,多个CPU代价,cpu核,脚本个 数,及执行时间长短。
●场景:测试用例1000条,-个用例执行1钟,- -
个测试人员执行需要1000分钟。通常我们会用人力成本换取时间成本,加几个人一-起执行,时间就会缩短。如果10人一起执行只需要100分钟,这就是一一种并行测试,分布式场景。
●解决: pytest分 布式执行插件: pytest-xdist, 多个CPU或主机执行
●前提:用例之间都是独立的,没有先后顺序,随机都能执行,可复运行不影响其他用例。
●安装: Pip3 install pytest-xdist
●多个CPU并行执行用例,直接加-n 3是并行数量: pytest -n 3 ●在多个终端下一起执行
●注意:只有超过秒级以上并发才显示效果,并且不只一个方法 ●https://pypi.org/project/pytest xdist/
第一步 在终端安装pytest-xdist
pip install pytest-xdist
在终端进入你要运行的文件目录
输入指令pytest -n 3
运行结果为
pytest-allure生成报告
打开下载网站
链接: allure
下载zip包,解压,进入bin目录运行,把bin目录加入path路径
打开pycharm 终端运行
pip install allure-pytest
安装一下 allure
在测试执行期间收集结果 pytest -s –q --alluredir=./result/ 测试完成后查看实际报告, 在线看报告。allure serve ./result/
cd 到你的测试文件目录下 执行 pytest -v -s --alluredir=./result 来收集你的测试数据
生成报告
同样在终端(你的测试文件目录下)
allure serve ./result
这样我们的报告就生成成功了
• 场景:希望在报告中看到测试功能,子功能或场景,测试步骤
• 解决:@Feature,story,step
• 步骤:
1. Import allure
2. 功能上加@allure.feature(‘功能名称’)
3. 子功能上加@allure.story(‘子功能名称’)
4. 步骤上加@allure.step(‘步骤细节’)
在方法上加@allure.feature(“模块名”),,
如果模块下还有小功能小分支 @allure.feature(“模块名-评论”)
@allure.story(“功能名-添加”) def test_me():
pass
@allure.feature(“模块名-评论”)
@allure.story(“功能名-编辑评论”) def test_me():
pass @allure.feature(“模块名-评论”)
@allure.story(“功能名-查看评论”)
import pytest
import allure
# web UI测试
# 打开浏览器(fixture) scope 是文件级(module) 和类级(class)
# 测试前购物车,支付,反馈(登录不登录)
@pytest.fixture(scope="module")
def driver():
with allure.step("1、开浏览器"):
print("打开浏览器")
yield driver
with allure.step("7、最后关闭浏览器"):
print("关闭浏览器")
@pytest.fixture(scope="module")
def login(driver):
with allure.step("2、登陆"):
print("juiceshop登陆了")
@allure.feature("反馈")
@allure.story("匿名")
@pytest.mark.parametrize("fk",["sdjfflsd","蝴蝶"])
def test_anno_repains(driver,fk):
with allure.step("3、匿名反馈"):
print("匿名反馈"+fk)
@allure.feature("购物车")
@pytest.mark.usefixtures("login")
def test_basket():
print("购物车")
@allure.feature("支付")
@pytest.mark.usefixtures("login")
def test_pay():
print("支付")
@allure.feature("反馈")
@allure.story("实名")
@pytest.mark.usefixtures("login")
def test_login_repains():
print("登陆后反馈")
可以看到报告中出现子级别
这样的报告 看着非常简洁 并附有说服力
报告的多样化
• 附加图片:
• allure.attach.file(source, name, attachment_type, extension):
• allure.attach.file("./result/b.png", attachment_type=allure.attachment_type.PNG
可以看到报告里生成了图片
• @allure.attach(‘具体文本信息’)
• @allure.link,@allure.issue并 @allure.testcase描述
• @allure.description;@allure.title;
import allure
@allure.feature("测试数字相等")
def test_01():
allure.attach("整数相等")
allure.attach.file("test_data.yaml", "数据", attachment_type=allure.attachment_type.YAML)
assert 1 == 1
@allure.feature("测试字典相等")
def test_02():
allure.attach("字典相等")
assert {"name": "linda"} == {'name': 'linda'}
allure.attach.file("bing.png","我喜欢",attachment_type=allure.attachment_type.PNG)
@allure.description("这个是要测试的点,包括功能测试。。。")
@allure.title("功能中安全测试")
@allure.link("http://114.116.97.187:3333/DVWA", "渗透测试用例")
@allure.issue("http://114.116.87.187:1081/zentao/user-login-L3plbnRhby8=.html", "bug地址")
@allure.testcase("http://114.116.87.187:1081/zentao/user-login-L3plbnRhby8=.html", "功能测试用例")
def test_03():
print("描述详细")
运行如下
可以看到里面的输入了文字 还有我们的title 还有描述都出来了