在做单接口的时候,比如有很多异常场景。之前在做测试时候,可以将数据保存在csv文件中,通过读取csv 文件的方式来进行参数化操作。 同样,在Python中也支持这样的操作。
注册接口
有不同的用户名密码
普通的做法,是写一个 for 循环,通过循环的方式来进行操作。
"""
测试注册接口
"""
# 定义测试数据 放在列表中, 总共有 5组数据。
userdata =[
("13211112222","123456"),
("","1234567"),
("13212341234",""),
("13212341234","12345678901234567890"),
("12345","123456")
]
import requests
# 使用上面的数据测试注册接口
base_url = "http://49.233.108.117:28019"
def test_regiseter():
for user in userdata:
register_url = base_url + "/api/v1/user/register"
jsondata = {
"loginName": user[0],
"password": user[1]
}
# 发送json格式数据
r = requests.post(url=register_url, json=jsondata)
# 打印状态码
print(r.status_code)
# 打印返回结果
print(r.json())
执行的时候虽然可以执行,但是结果中只有一条用例。
这样肯定是不行的。因为我准备5条数据, 希望运行的时候是5个用例。
pytest 参数化
pytest 框架提供了一种专门的参数化功能。只需要调用pytest 对应的方法就可以。
https://docs.pytest.org/en/7.1.x/how-to/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions
使用pytest 内置的参数化功能。
"""
测试注册接口
"""
# 定义测试数据 放在列表中, 总共有 5组数据。
userdata =[
("13211112222","123456"),
("","1234567"),
("13212341234",""),
("13212341234","12345678901234567890"),
("12345","123456"),
("中文","123456")
]
import requests
import pytest
# 使用上面的数据测试注册接口
base_url = "http://49.233.108.117:28019"
@pytest.mark.parametrize("username,password",userdata)
def test_user_regisgter(username,password):
# 执行打开 还是和原来一样
url = base_url + "/api/v1/user/register"
bodydata = {
"loginName": username,
"password": password
}
r = requests.post(url=url,json=bodydata)
print(f"请求数据: {r.request.body}")
print(f'返回结果: {r.status_code}, {r.json()}')
执行,可以看到 userdata 中有多少组数据, 就生成多少个测试用例。
这个就是 pytest 参数的功能。
- @pytest.mark.parametrize 固定写法
- “username,password” 跟 列表中的每一组数据 对应 (“13211112222”,”123456”)
username — 13211112222 password —123456
里面编写自动化代码还是不变。
添加对应的断言
不同的请求数据,断言结果也不一样,也可以将断言结果直接放在定义的数据中。
userdata =[
("13211112222","123456",500,"用户名已经存在!"),
("","1234567",510,"登录名不能为空!"),
("13212341234","",510,"密码不能为空")
]
因为又添加了两个数据,所以引用的时候,数据要保持一致。
"""
测试注册接口
"""
# 定义测试数据 放在列表中, 总共有 5组数据。
userdata =[
("13211112222","123456",500,"用户名已存在!"),
("","1234567",510,"登录名不能为空"),
("13212341234","",510,"密码不能为空")
]
import requests
import pytest
# 使用上面的数据测试注册接口
base_url = "http://49.233.108.117:28019"
@pytest.mark.parametrize("username,password,errCode,errMsg",userdata)
def test_user_regisgter(username,password,errCode,errMsg):
# 执行打开 还是和原来一样
url = base_url + "/api/v1/user/register"
bodydata = {
"loginName": username,
"password": password
}
r = requests.post(url=url,json=bodydata)
print(f"请求数据: {r.request.body}")
print(f'返回结果: {r.status_code}, {r.json()}')
# 添加断言
assert r.json()["resultCode"] == errCode
assert r.json()["message"] == errMsg
添加对应的断言。执行结果。
当数据量比较少的时候,可以将数据直接写在代码中,但是如果异常场景的数据很多,数据比较多的时候就不太适合放在代码中。
可以考虑将数据放在文件中。
csv数据
代码生成测试数据
将可能的数据放在先列举出来。通过python代码值生成不同排列组合的场景。
username = ["13212341234","","1234","132123412341","123456785678"]
password = ["","123456","1","1234567890123456789"]
testuser = []
for name in username:
for passwd in password:
# 组合一个场景的数据
user = (name,passwd)
# 将数据放在列表中
testuser.append(user)
# 循环完成之后,查看生成的数据
print(testuser)
# 将生成的数据使用参数化
import pytest
import requests
base_url = "http://49.233.108.117:28019"
# 使用参数化功能
@pytest.mark.parametrize("username,password",testuser)
def test_data_register(username,password):
url = base_url + "/api/v1/user/register"
bodydata = {
"loginName": username,
"password": password
}
r = requests.post(url=url, json=bodydata)
print(f"请求数据: {r.request.body}")
print(f'返回结果: {r.status_code}, {r.json()}')
代码生成添加断言
不同的数据 服务器返回结果也不一样。
编写编写对应的代码
username = ["13212341234","","1234","132123412341","123456785678"]
password = ["","123456","1","1234567890123456789"]
testuser = []
for name in username:
for passwd in password:
# 添加message 断言
if name == "":
message = "登录名不能为空"
elif passwd == "":
message = "密码不能为空"
elif len(name) != 11:
message = "请输入正确的手机号!"
else:
message= "用户名已存在!"
# 组合一个场景的数据
user = (name,passwd,message)
# 将数据放在列表中
testuser.append(user)
# 循环完成之后,查看生成的数据
print(testuser)
# 将生成的数据使用参数化
import pytest
import requests
base_url = "http://49.233.108.117:28019"
@pytest.mark.parametrize("username,password,message",testuser)
def test_data_register(username,password,message):
url = base_url + "/api/v1/user/register"
bodydata = {
"loginName": username,
"password": password
}
r = requests.post(url=url, json=bodydata)
print(f"请求数据: {r.request.body}")
print(f'返回结果: {r.status_code}, {r.json()}')
# 针对message 断言
assert r.json()["message"] == message
运行,可以看到执行用例,并发现一个bug。
数据保存到文件
最好也将测试数据保存到csv文件中。csv数据文件可以jmeter 或者postman 结合一起使用。
import pytest
import requests
import csv
username = ["13212341234", "", "1234", "132123412341", "123456785678"]
password = ["", "123456", "1", "1234567890123456789"]
test_user = []
for name in username:
for passwd in password:
# 添加message 断言
if name == "":
message = "登录名不能为空"
elif passwd == "":
message = "密码不能为空"
elif len(name) != 11:
message = "请输入正确的手机号!"
else:
message = "用户名已存在!"
# 组合一个场景的数据
user = (name, passwd, message)
# 将数据放在列表中
test_user.append(user)
# 循环完成之后,查看生成的数据
print(test_user)
# 将生成的数据使用参数化
base_url = "http://49.233.108.117:28019"
@pytest.mark.parametrize("username,password,message", test_user)
def test_data_register(username, password, message):
url = base_url + "/api/v1/user/register"
body_data = {
"loginName": username,
"password": password
}
r = requests.post(url=url, json=body_data)
print(f"请求数据: {r.request.body}")
print(f'返回结果: {r.status_code}, {r.json()}')
# 针对message 断言
assert r.json()["message"] == message
# 运行的时候,运行一条用例 保存一条数据
with open('register_data.csv',encoding='utf8',mode='a',newline='') as f:
cw = csv.writer(f)
cw.writerow([username,password,message])
执行,完成之后可以看到,数据已经放在测试数据已经生成。
但是,我们希望将测试数据文件 都放在 testdata 目录下。便于统一管理。
os 模块处理路径
默认数据文件使用的是相对路径生成。 我们希望数据放在指定的目录下。一种解决办法使用绝对路径
。这样的写法,有问题。 当代码在我的电脑上可以执行。但是在你的电脑就不能执行了。
因为你的电脑里没有这样的路径。
file 全局变量
python中内置的有 全局变量。 返回文件在自己电脑上的绝对路径。
def get_root_dir():
"""
获取项目的根目录
:return:
"""
# 当前文件在系统中的绝对路径
print(__file__) # C:\Users\zengy\PycharmProjects\pythonProject13\common\file_dir.py
if __name__ == '__main__':
get_root_dir()
os.path.dirname() 文件目录
使用os模块中的 path中的dirname 可以返回目录路径。
"""
处理文件路径
"""
import os
def get_root_dir():
"""
获取项目的根目录
:return:
"""
# 当前文件在系统中的绝对路径
print(__file__) # C:\Users\zengy\PycharmProjects\pythonProject13\common\file_dir.py
# 当前文件所在的目录
dir_name = os.path.dirname(__file__)
print(dir_name) # C:\Users\zengy\PycharmProjects\pythonProject13\common
# 当前目录的上一层目录
root_dir = os.path.dirname(dir_name)
print(root_dir) # C:\Users\zengy\PycharmProjects\pythonProject13
if __name__ == '__main__':
get_root_dir()
- dirname() 返回文件所在的目录路径。
os.path.jion() 路径拼接
找到项目的根目录之后,可以写代码来指定文件路径。 ```python “”” 处理文件路径 “”” import os def get_root_dir(): “”” 获取项目的根目录 :return: “””当前文件在系统中的绝对路径
print(file) # C:\Users\zengy\PycharmProjects\pythonProject13\common\file_dir.py当前文件所在的目录
dirname = os.path.dirname(_file) print(dir_name) # C:\Users\zengy\PycharmProjects\pythonProject13\common当前目录的上一层目录
root_dir = os.path.dirname(dir_name) print(root_dir) # C:\Users\zengy\PycharmProjects\pythonProject13返回
return root_dir
def demo():
# 将数据保存在 testdata 目录下
root_dir = get_root_dir()
# testdata 的路径
data_dir = os.path.join(root_dir,"testdata")
print(data_dir) # C:\Users\zengy\PycharmProjects\pythonProject13\testdata
csvfiles = os.path.join(data_dir,"单接口测试数据")
print(csvfiles)
if name == ‘main‘: demo()
- join 将两个路径拼接在一起。
<a name="LHcIg"></a>
## os.mkdir() 创建目录
```python
"""
处理文件路径
"""
import os
def get_root_dir():
"""
获取项目的根目录
:return:
"""
# 当前文件在系统中的绝对路径
print(__file__) # C:\Users\zengy\PycharmProjects\pythonProject13\common\file_dir.py
# 当前文件所在的目录
dir_name = os.path.dirname(__file__)
print(dir_name) # C:\Users\zengy\PycharmProjects\pythonProject13\common
# 当前目录的上一层目录
root_dir = os.path.dirname(dir_name)
print(root_dir) # C:\Users\zengy\PycharmProjects\pythonProject13
# 返回
return root_dir
def demo():
# 将数据保存在 testdata 目录下
root_dir = get_root_dir()
# testdata 的路径
data_dir = os.path.join(root_dir,"testdata")
print(data_dir) # C:\Users\zengy\PycharmProjects\pythonProject13\testdata
csvfiles = os.path.join(data_dir,"单接口测试数据")
print(csvfiles)
# 创建目录 如果路径已经存在,再次创建,会报错
if not os.path.exists(csvfiles): # 如果这个路径不存在
os.mkdir(csvfiles) # 创建目录
if __name__ == '__main__':
demo()
- os.path.exists(csvfiles) 判断文件路径是否存在
- os.mkdir(csvfiles) 创建目录
修复之前的代码bug
之前读取 csv文件的时候,路径使用绝对路径,现在改为 获取根目录之后进行路径拼接。 ```python “”” 这个文件中存放自己定义的一些常用的工具函数。 生成测试数据 “”” import random import csv from common.file_dir import get_root_dir import os def get_china_code():获取项目的根目录
root_dir = get_root_dir()路径拼接
gb2260 = os.path.join(root_dir,’testdata’,’gb2260.csv’) print(gb2260)as f 别名 f相当于是 前面open打开的文件 绝对路径字符串 前添加r
with open(file=gb2260,encoding='utf8',mode='r') as f: # 读取完成之后会将所有的内容放在一个列表对象中 rows = csv.reader(f) # 定义空列表 allcodes = [] # 循环列表 for row in rows: # print(row,type(row)) code = "".join(row) # 每次取到一个数据,讲这个数据放在列表中 allcodes.append(code) # print(code,type(code)) # for循环执行完,查看列表的内容 # print(allcodes) # 随机从这么多的地区码中选择一个 返回。 random_code = random.choice(allcodes) return random_code
def get_phone(): “”” 自动随机生成一个手机号码
:return:
"""
pre_phones = ["130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
"170", "172", "179",
"150", "151", "155", "156",
"188",
"199"]
# 随机取号段
pre_3 = random.choice(pre_phones)
# print(f"随机选择号段 {pre_3}")
# 生成后8位
last_8 = ""
for i in range(8):
# 随机生成一个数字n
n = random.randint(0, 9)
# 拼接字符串 将n转换为字符串
last_8 = last_8 + str(n) # 每次随机取到一个数字,转换字符串之后 放到last_8 后面
# print(f"生成后8位 {last_8}")
# 将前3位后8位 组合一起
phone = pre_3 + last_8
return phone
def get_id(): “”” 随机生成身份证号
:return:
"""
# 函数定义在函数的内部 闭包
def pre_6():
n = get_china_code()
# n = ""
# for i in range(6):
# # 循环6次 每次生成1个随机数字,转换位字符串进行拼接
# n = n + str(random.randint(0, 9))
# 循环执行完成
return n
def ymd_8(startyear=1922, endyear=2021):
# 生成出生的年 随机生成
year = random.randint(startyear, endyear)
# print(f"生成年份 {year}")
# 生成月份 1-12
month = random.randint(1, 12)
# 如果月份在1-9 之间,前面需要补0
if month < 10:
month = "0" + str(month) # 转换位字符串之后前面 补0
# print(f"生成的月份 {month}")
# 1,3,5,7,8,10,12 天数 31天
if int(month) in [1, 3, 5, 7, 8, 10, 12]:
day = random.randint(1, 31) #
# 4,6,9,11 月 30天
elif int(month) in [4, 6, 9, 11]:
day = random.randint(1, 30)
# 闰年 2月份 29天
elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
day = random.randint(1, 29)
# 以上都不符合,那就是平年
else:
day = random.randint(1, 28)
# 如果day 在1-9 之间 前面补0
if day < 10:
day = "0" + str(day)
# print(f"生成的日期 {day}")
# 转换为字符串 进行拼接
ydm = str(year) + str(month) + str(day) # 等价于 f"{year}{month}{day}"
return ydm
def last_4():
nums = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X']
# 前三位 数字
n3 = ""
for i in range(3):
n3 = n3 + str(random.randint(0, 9))
# 第四位 随机从 nums 选择一个
n4 = random.choice(nums)
# 将n3 和 n4 拼接之后返回
return n3 + n4
# 生成身份证id
id = pre_6() + ymd_8() + last_4()
return id
if name == ‘main‘: print( get_china_code())
# 测试生成手机号
# print(get_phone())
# 测试生成身份证id
# print(get_id())
<a name="WvNS8"></a>
# csv 数据保存到指定路径下
```python
import pytest
import requests
import csv
from common.file_dir import get_root_dir
import os
username = ["13212341234", "", "1234", "132123412341", "123456785678"]
password = ["", "123456", "1", "1234567890123456789"]
test_user = []
for name in username:
for passwd in password:
# 添加message 断言
if name == "":
message = "登录名不能为空"
elif passwd == "":
message = "密码不能为空"
elif len(name) != 11:
message = "请输入正确的手机号!"
else:
message = "用户名已存在!"
# 组合一个场景的数据
user = (name, passwd, message)
# 将数据放在列表中
test_user.append(user)
# 循环完成之后,查看生成的数据
print(test_user)
# 将生成的数据使用参数化
base_url = "http://49.233.108.117:28019"
@pytest.mark.parametrize("username,password,message", test_user)
def test_data_register(username, password, message):
url = base_url + "/api/v1/user/register"
body_data = {
"loginName": username,
"password": password
}
r = requests.post(url=url, json=body_data)
print(f"请求数据: {r.request.body}")
print(f'返回结果: {r.status_code}, {r.json()}')
# 针对message 断言
assert r.json()["message"] == message
# 将数据保存到 testdata 目录下
root_dir = get_root_dir() # 项目根目录
# 保存数据的目录 testdata/test_regsiter
data_dir = os.path.join(root_dir,"testdata","test_register")
# 如果目录不存在 创建目录
if not os.path.exists(data_dir):
os.mkdir(data_dir)
# 数据文件路径 testdata/test_regsiter/register_data.csv
rigeter_data_file = os.path.join(data_dir,"register_data.csv")
# 运行的时候,运行一条用例 保存一条数据
with open(rigeter_data_file,encoding='utf8',mode='a',newline='') as f:
cw = csv.writer(f)
cw.writerow([username,password,message])
执行完成之后,数据保存到指定的目录中。
总结
数据驱动 主要就是使用 @pytest.mark.parametrize(“username,password,message”, test_user)
pytest中内置的 参数化功能进行数据驱动操作。