前言
简单灵活,容易上手;支持参数化; 测试用例的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
打开pycharm 打开终端 输入 pip install pytest 来安装
没有 自定义版本 他会安装最新版本
cd 进入你要执行文件的目录
直接终端CD进入目录 输入pytest
就可运行 全部的test测试文件
可以看到 我是再unittest目录下执行 我要执行的文件
并且可以看到已经全部执行了
图中绿色的点 代表 执行成功 黄色的S代表 跳过
直接终端CD进入目录 输入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 -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个没有被 选择执行上。
-S 可以输出每一个方法执行的结果
-v 显示信息强度 也就是 结果里面 每一步的执行过程
可以 - v -v
也可以 -v -v -v 最高强度三个-v
pytest -v -s test_pytest_frame.py::TestPytest (TestPytest这个类下面的所有方法)
• 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'}
• 步骤:
• 解决: • 使用fixture中参数autouse=True实现
• 步骤:在方法上面加@pytest.fixture(autouse=True)
自动应用 示例如下
• 使用@pytest.mark.usefixtures
• 步骤:在测试方法上加@pytest.mark.usefixtures(“方法名")
通过装饰器 示例如下
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 , 调用一次,给你一个反馈,调用第二 次,反馈从第一次结束语句开始给你反馈
测试用例如下:
pytest运行顺序
1先运行16行的
2在16行找到的参数open_close_broswer
3找到这个参数再12行
4返回这个参数执行并带入行
5执行yield
6执行17行
717行调用12行的参数
8执行关闭浏览器
#在一个 方法实现打开关闭浏览器,并且是初始化销毁
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")
让他只执行一次
后面在调用 他不执行
• 测试离不开数据,为了数据灵活一般,数据都是通过参数传的
• 解决: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
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
即可
●场景:测试用例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
运行结果为
链接: allure
下载zip包,解压,进入bin目录运行,把bin目录加入path路径
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 还有描述都出来了