学习更新ing…
目录
相关知识
基本知识
命名规则
py文件以及函数名需要以test_开头
运行方式
整体运行或者在终端运行:新建pytest.ini并设置,使用pytest命令
前后置处理
setup/teardown
模块级:(函数级同理)
def setup_module():
print("准备测试数据")
def teardown_module():
print("清理测试数据")
def test_douban1():
pass
def test_douban2():
pass
if __name__=='__main__':
pytest.main()
类级:(方法级同理)
class TestDouban():
def setup_class(self):
print("准备测试数据")
def teardown_class(self):
print("清理测试数据")
def test_douban1(self):
pass
def test_douban2(self):
pass
if __name__=='__main__':
pytest.main()
夹具fixture
参数scope: 默认function
(scope=“function”)每一个函数或方法都会调用
(scope=“class”) 每一个类调用一次
(scope=“module”) 每一个.py文件调用一次
(scope=“session”)是多个文件调用一次,.py文件就是module
参数autouse: 默认True,即所有方法都调用前置处理
@pytest.fixture(scope="function",autouse=False)
def func():
print("前置步骤")
def test_douban1(func):
pass
def test_douban2():
pass
if __name__=='__main__':
pytest.main()
parametrize参数化
传统:一个测试用例对应一个测试函数;
参数化:数据驱动 ,实现测试数据和测试函数分离,只需要一个测试函数,n个测试用例调用n次测试函数,生成n次测试结果
单参数:
@pytest.mark.parametrize('num', [1, 2, 3])
def test_login(self, num):
print(f'当前测试用例数据:{num}')
多参数:
@pytest.mark.parametrize('num, expect', [(1, True), (2, True), (3, False)])
def test_login(self, num, expect):
print(f'当前测试用例数据:{num}')
assert func(num) == expect
数据库参数化
config.ini内容:
# 数据库配置文件,多种类型存储数据
[dbinfo]
dbinfo ={
"host":"...",
"user":"...",
"password":"...",
"port":3306
}
封装数据库连接操作:
使用pymysql基本步骤:
- 创建连接对象db
- 创建游标对象cur
- 执行sql
- 关闭游标、连接对象
class ReadFile():
def __init__(self):
self.conf = ConfigParser(strict=False) # 防止读取重复配置项
self.conf.read('config.ini', encoding='utf-8')
# 封装读取config配置文件
def getValue(self, section='host', option='session_host'):
value = self.conf.get(section, option)
return value
class Connect():
def __init__(self, database='dushuwu'):
self.dbinfo = eval(ReadFile().getValue('dbinfo', 'dbinfo'))
# print(dbinfo)
# 数据库连接对象
self.db = pymysql.Connect(
database=database,
**self.dbinfo,
cursorclass=pymysql.cursors.DictCursor
)
# 定义游标
self.cur = self.db.cursor()
if __name__ == '__main__':
if __name__ == '__main__':
con = Connect()
sql = 'select book_name from book where id=134'
con.cur.execute(sql)
data = con.cur.fetchall() #获取数据
con.cur.close()
con.db.close()
print(f'从数据库中获取的数据:{data}')
示例代码:
import os.path
import pandas as pd
import pytest
import yaml
import json
import codecs
from selenium import webdriver
from selenium.common import TimeoutException, NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
@pytest.fixture(scope="module")
def func():
print("前置操作")
driver = webdriver.Firefox() # 选择火狐浏览器
driver.implicitly_wait(10)
# driver.maximize_window()
return driver
# 读取yaml文件的方法
def get_caseData():
return_value = []
data_file_path = os.path.dirname(os.path.abspath(__file__)) + '/cases.yaml'
print(data_file_path)
_is_yaml_file = data_file_path.endswith((".yml", ".yaml"))
with codecs.open(data_file_path, 'r', 'utf-8') as f:
# 从YAML或JSON文件中加载数据
if _is_yaml_file:
data = yaml.safe_load(f)
else:
data = json.load(f)
for i, elem in enumerate(data):
if isinstance(data, dict):
key, value = elem, data[elem]
if isinstance(value, dict):
case_data = []
for v in value.values():
case_data.append(v)
return_value.append(tuple(case_data))
else:
return_value.append((value,)) # 单元素的元组
return return_value # 返回结果[('byhy', 'sdfsdf'), ('byhy1', 'sdfsdf1')]
# 读取excel
def get_excelData():
excel_file = os.path.dirname(os.path.abspath(__file__)) + r'/test_p_readYE_searchName.xlsx'
if not os.path.exists(excel_file):
raise ValueError("File not exists")
# 初始化
s = pd.ExcelFile(excel_file)
# 解析Excel Sheet
df = s.parse('Sheet1')
# 以list格式返回数据
return df.values.tolist()
# def get_dfData():
# xls = pd.ExcelFile(r'../data/test_p_readConfig_searchName.xlsx', engine='openpyxl')
# xls1 = xls.parse(xls.sheet_names[0])
# df = xls1.to_dict('records')
# return df
# @pytest.mark.parametrize("username,password", get_caseData()) # 读取yaml
@pytest.mark.parametrize("search_word", get_excelData()) #读取excel
# def test_param(username, password, func):
def test_param(search_word, func):
# print(f'获取的yaml_username数据:{username}')
# print(f'获取的yaml_password数据:{password}')
print(f'获取的excel_searchWord数据:{search_word}')
driver = func
driver.get('http://localhost/sign.html')
driver.find_element(By.XPATH, '//*[@id="username"]').clear()
# driver.find_element(By.XPATH, '//*[@id="username"]').send_keys(username)
# driver.find_element(By.XPATH, '//*[@id="password"]').send_keys(password)
driver.find_element(By.XPATH, '//*[@id="username"]').send_keys('byhy')
driver.find_element(By.XPATH, '//*[@id="password"]').send_keys('sdfsdf')
driver.find_element(By.XPATH, '/html/body/div/div[2]/div/div[1]/div[3]/div/button').click()
try:
WebDriverWait(driver, 20).until(
EC.visibility_of_element_located((By.XPATH, '/html/body/div/aside/a'))
)
except TimeoutException:
print("Timeout occurred while waiting for the element to be visible.")
except NoSuchElementException:
print("Element was not found on the page.")
driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/input').clear()
# driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/input').send_keys('sz_001200')
driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/input').send_keys(search_word)
driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/span/button').click()
def teardown_function():
print("后置操作")
if __name__ == '__main__':
pytest.main()
jenkins持续集成
jenkins框架部署自动化测试学习:B站视频
通过war包运行jenkins:war包目录下输入
java -jar jenkins.war --httpPort=8080
当看到 Jenkins is fully up and running 就表示 jenkins 已经启动完成了
jenkins配置:参考博客
整体步骤:
- 将上传到gitlab的项目url关联到jenkins项目中
- jenkins项目配置,主要是定时构建,构建后发送邮件
代码
整体框架1
import pytest
from xToolkit import xfile
#一行代码读取所有的测试用例信息
测试用例集=xfile.read("接口测试用例.xls").excel_to_dict(sheet=1)
print(测试用例集)
@pytest.mark.parametrize("case_info",测式用例集)
def test_case_exec(case_info):#这个方法--专门用于执行测试用例
print("模拟-发起接口请求:",case_info)
#调用pytest框架
if __name__=='main':
pytest.main(['-s',
'-v',
'--capture=sys', #保留系统输出
'api_pytest_framework.py',
'--clean-alluredir',
'--alluredir=allure-results'])#生成测试数据-用于生成htmL报告
#结合aLLure生成测试报告
os.system(r"allure generate -c-o 测试报告")
整体框架2
excel填写测试用例:
import pytest
import pandas as pd
import requests
import json
# 使用ExcelFile读取文件
xls = pd.ExcelFile(r'Pytest_case.xlsx')
# 尝试读取第一个工作表
df = xls.parse(xls.sheet_names[0])
df = df.to_dict('records')
# print("读取到df的值:", df)
@pytest.mark.parametrize("case_info",df)
def test_case_exec(case_info):
# print("每个case_info的值:", case_info)
响应 = requests.request(url=case_info['接口URL'],
method=case_info['请求方式'],
headers=json.loads(case_info['URL参数']),
json=json.loads(case_info['JSON参数']) if case_info['请求方式'] == 'post' else None
)
print(响应.json())
# assert 响应.status_code == case_info['预期状态码'],'响应状态码不匹配'
if __name__=='__main__':
pytest.main(['-vs',
'--capture=sys'])
在python代码中读取excel
excel中数据如下:
# 使用pandas的ExcelFile读取文件
xls = pd.ExcelFile(r'Pytest_case.xlsx')
# 读取第一个工作表
df = xls.parse(xls.sheet_names[0])
# 将DataFrame转换为字典类型
df = df.to_dict('records')
request发起请求
与postman
三种参数格式:params,data,json对应的postman中的参数位置
与java代码
第一种params,代码格式如下:
postman中设置参数如下:
第二种data,代码格式如下:
postman中设置参数如下:
第三种json,代码格式如下:
postman中设置参数如下:
书写格式
# pytest参数化机制多用例执行
@pytest.mark.parametrize("case_info",测试用例集)
def test_case_exec(case_info):#这个方法--专门用于执行测试用例
#发起接口请求
print("发起接口请求:",case_info)
接口响应=requests.request(
url=case_info['接口URL'],
method=case_info['请求方式'],
params=eval(case_info['URL参数']),
data=case_info['表单参数'],
json=case_info['JSON参数']
)
print("接口响应内容:",接口响应.text)
断言
常用断言类型
首先需要在excel中添加预期状态列
#判定接口测试结果[断言]
assert 接口响应.status_code==case_info['预期状态码'],'响应状态码不匹配'
提取参数
需要在excel中预先填写提取参数变量名($…直接匹配所有元素),然后获取值并赋给字典dic的一个键,这个键的名称由case_info[“提取参数”]的值决定。
if case_info["提取参数"]:# 不为空true执行下面代码
t_lst=jsonpath.jsonpath(应答.json(),"$.." + case_info["提取参数"])
#dic.update({case_info["提取参数"],t_lst[o]})
dic[case_info["提取参数"]]=t_lst[0]
使用提取的参数
修改excel中的url格式(添加参数)
代码中将url中的请求参数替换掉
url=case_info["接口URL"]
if "$" in url:
url=Template(url).substitute(dic)
定制测试报告
定制测试报告标题、描述等信息
import pytest,os,requests,allure
def test_case_exec(case_info):#这个方法--专门用于执行测试用例
#把测试报告结果--和测试用例信息关联起来alLure测试报告动态定制化
allure.dynamic.title(case_info['用例编号'])
allure.dynamic.description(case_info['描述'])