基本框架的主要目录结构
配置虚拟环境
默认打开项目,没有配置环境。手动配置一下
找到python解释器的配置
点击【add】添加新的环境配置
选择虚拟环境
点击创建。创建成功之后可以看到项目使用的python为虚拟环境的python。
项目中 venv
创建common 包
创建包,因为现在是git项目,会提示是否添加到git中,选择【ADD】
定义常用函数
常用工具函数 获取项目的根目录,以及生成测试手机号,测试身份证id这些函数。
common包下创建一个 file_handler.py 这个文件中存放一些文件路径相关操作的函数。
import os
def get_root_dir():
"""
生成项目的根目录
:return:
"""
# 生成当前文件的绝对路径
p1 = os.path.abspath(__file__)
p2 = os.path.dirname(p1)
p3 = os.path.dirname(p2)
return p3
if __name__ == '__main__':
root_dir = get_root_dir()
print(f"项目根目录 {root_dir}")
提交到代码仓库
文件编写成功之后,要及时提交代码。
选择提交按钮
输入对应的提交信息。
如果提交的时候,选择的是【Commit】,没有推送到服务器。
那么手动推动到服务器(如果上面一步已经提交并推送过了,就不需要再次push)
点击【Git】—【Log】—【Master】—右键—【Push】 推送到服务器
推送成功之后,可以看到 已经提交成功。
生成随机手机号码
按照上面的操作,创建新文件
common/my_tools.py 将生成手机号码封装到函数中
"""
一些常用的工具
"""
# 导入Python内置的random 模块
import random
def get_phone():
# 随机从列表中选择一个值
nums = ["132", "130", "131", "133", "134", "135", "136", "137", "138", "139",
"155", "157", "159"]
# 随机从列表中选择一个元素 作为手机号的前3位。
num = random.choice(nums)
# 定义空的字符串
p8 = ""
# 生成8个随机数字
for i in range(8):
# 每次循环都生成一个随机数字
n = random.randint(0, 9)
# 将数字n跟 p8 进行拼接 因为是n是数字,将n转换位字符串进行拼接
p8 = p8 + str(n)
return num+p8
if __name__ == '__main__':
phone = get_phone()
print(phone)
生成身份证id
创建 config 配置包
config 主要用来存放一些配置文件。
将gb2260.csv文件放在config 配置包中。
编写生成身份证id函数
在common/mytools.py 文件中 生成随机的身份证号。
"""
一些常用的工具
"""
# 导入Python内置的random 模块
import random
import csv
def get_phone():
# 随机从列表中选择一个值
nums = ["132", "130", "131", "133", "134", "135", "136", "137", "138", "139",
"155", "157", "159"]
# 随机从列表中选择一个元素 作为手机号的前3位。
num = random.choice(nums)
# 定义空的字符串
p8 = ""
# 生成8个随机数字
for i in range(8):
# 每次循环都生成一个随机数字
n = random.randint(0, 9)
# 将数字n跟 p8 进行拼接 因为是n是数字,将n转换位字符串进行拼接
p8 = p8 + str(n)
return num+p8
def get_id():
from common.file_handler import get_root_dir
# 获取项目的根目录
root_dir = get_root_dir()
import os
# 路径拼接
csvfile = os.path.join(root_dir,'config/gb2260.csv')
def get_code():
"""
主要是生成随机的前6位
"""
# 创建一个空的列表
all_code = []
# 读取 gb2260.csv 文件
with open(file=csvfile, encoding='utf8', mode='r') as f:
# 使用csv 创建读取对象
cr = csv.reader(f)
for code in cr:
# print(code, type(code),code[0])
# 每次获取一个code 将code值存放在 all_code 中
all_code.append(code[0])
# 等文件读取完成之后 all_code中存放了所有的 code值
# print(all_code)
# 使用random 随机从 all_code 中选择一个
cd = random.choice(all_code)
# print(f"随机选中地区码:{cd}")
return cd
def get_ymd():
"""
主要是生成随机的4位年 2位月 2位 日
"""
# 生成年月日
# 生成年份
y = random.randint(1950, 2020)
# print(f"生成的年份:{y}")
# 生成月份
m = random.randint(1, 12)
if m < 10:
# 月份前面补0,将数字转换位字符串
m = "0" + str(m)
# print(f"生成的月份:{m}")
# 生成天
# 根据月份和年份
# 1 当月份为 1,3,5,7,8,10,12 月,天数31天
if int(m) in [1, 3, 5, 7, 8, 10, 12]:
d = random.randint(1, 31)
# 2,当月份为 4,6,9,11 月 天数是30天
elif int(m) in [4, 6, 9, 11]:
d = random.randint(1, 30)
# 3.2月份分为平年和闰年, 如果是闰年的29天
# 公元年分为4的倍数但非100的倍数 或者 为400的倍数
elif (y % 4 == 0 and y % 100 != 0) or y % 400 == 0:
d = random.randint(1, 29)
# 以上都不符合,那就是平年的2月份
else:
d = random.randint(1, 28)
# 如果d小于10 ,需要补0
if d < 10:
d = "0" + str(d)
# print(f"生成的日期:{d}")
return str(y) + str(m) + str(d)
def get_last():
"""
生成身份证号的后4位
"""
l3 = ""
# 循环三次
for i in range(3):
# 每次都生成一个随机数字进行拼接
l3 = l3 + str(random.randint(0, 9))
# 最后一位有可能是X,也可能是数字
l1 = random.choice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "X"])
# print(f"生成的最后四位: {l3}{l1}")
return str(l3) + str(l1)
code = get_code()
ymd = get_ymd()
last = get_last()
return str(code)+str(ymd)+str(last)
if __name__ == '__main__':
phone = get_phone()
user_id = get_id()
print(user_id)
编写完成之后,测试无误,提交代码。
后面根据业务需要,有新的带有某些功能的代码 都可以放在common 包下。
创建test_cases 测试用例包
将所有的测试用例都放在test_cases 包下面。
我们目前的测试场景主要
- 正常业务流程 test_business
- 单接口的正常,异常场景 test_single_api
安装依赖包
因为每个项目使用虚拟环境都是独立,所以在新项目中需要安装 requests, pytest。 ```python pip install pytest
pip install requests
<a name="wE9rx"></a>
## 编写测试代码
<a name="wMSGJ"></a>
### 正常业务流程
代码中需要生成测试手机号,通过引用common 包下创建的代码。
```python
from common.mytools import get_phone
# 随机生成手机号码
phone = get_phone()
进行上下游传参,我们将数据定义在字典中,如果执行多个文件,上下游传参的字典放在config 包下
jsondata = {
"token":None,
"goodsid":None,
}
编写测试用例,通过导入的方式将 手机号,jsondata 导入即可。
"""
下单业务主流程
"""
import requests
import random
from common.mytools import get_phone
from config.mydata import jsondata
# 随机生成手机号码
phone = get_phone()
def test_register():
# 定义注册用户的地址
url = "http://49.233.108.117:28019/api/v1/user/register"
# 定义请求数据
bodydata = {
"loginName": str(phone),
"password": "123456"
}
print(bodydata)
# 发送请求 url= 表示请求地址, json= 请求数据为json格式
r = requests.post(url=url, json=bodydata)
# r 是整个服务器返回的结果
print("状态码:", r.status_code)
# 服务器返回结果
print("结果:", r.json())
def test_login():
url = "http://49.233.108.117:28019/api/v1/user/login"
bodydata = {
"loginName": str(phone),
"passwordMd5": "E10ADC3949BA59ABBE56E057F20F883E"
}
print(bodydata)
r = requests.post(url, json=bodydata)
print(r.status_code)
print(r.json())
# 将token提取出来 放在 字典格式数据中 - 上游传参
jsondata["token"] = r.json()["data"]
def test_search():
url = "http://49.233.108.117:28019/api/v1/search"
# 信息头数据 下游接口中使用到这个值
headerdata = {
"token": jsondata["token"]
}
# 请求参数
querydata = {
"keyword": "iphone"
}
# 发送get 请求 params get请求的参数, headers 请求头数据
r = requests.get(url=url,params=querydata,headers=headerdata)
print(r.status_code)
print(r.json())
# 所有的商品 存放在一个列表中
all_goods = r.json()['data']['list']
# 创建空的列表
gids = []
# 循环列表 拿到每一个商品
for goods in all_goods:
# 从商品中访问 goodsId
gid = goods["goodsId"]
print(gid)
# 将商品id 放在列表中
gids.append(gid)
# 循环结束 可以看到所有的商品id
print(gids)
# 随机选择一个 作为参数
jsondata["goodsid"] = random.choice(gids)
def test_add_cart():
url = "http://49.233.108.117:28019/api/v1/shop-cart"
# 信息头数据 下游接口中使用到这个值
headerdata = {
"token": jsondata["token"]
}
bodydata = {
"goodsCount": random.randint(1,5),
"goodsId": jsondata["goodsid"]
}
r = requests.post(url,json=bodydata,headers=headerdata)
print(r.status_code)
print(r.json())
执行,用例
用例通过之后,可以再次提交代码。
单接口测试
需要将数据保存到csvdata 目录中,csvdata 目录的操作最好也需封装一下。
在common包下 file_handler.py 文件中再新建函数
import os
def get_root_dir():
"""
生成项目的根目录
:return:
"""
# 生成当前文件的绝对路径
p1 = os.path.abspath(__file__)
p2 = os.path.dirname(p1)
p3 = os.path.dirname(p2)
return p3
def get_csvdata_dir():
root_dir = get_root_dir()
# 路径拼接
csvdata_dir = os.path.join(root_dir,'csvdata')
# 如果路径不存在 那就创建
if not os.path.exists(csvdata_dir):
os.mkdir(csvdata_dir)
return csvdata_dir
if __name__ == '__main__':
root_dir = get_root_dir()
print(f"项目根目录 {root_dir}")
csvdata = get_csvdata_dir()
print(f"csvdata目录路径: {csvdata}")
编写单接口测试代码
import pytest
import requests
tokens =["","e18de36f-d9ce-47e6-a2aa-1cf6508ec10","e18de36f-d9ce-47e6-a2aa-1cf6508ec10b","240d17fe-9a54-4d78-b73d-7600af599174"]
titles = ["","1234","1234567890"]
tabs=["ask","share","dev","job","","jk"]
contents = ["","xxxx"]
#存放所有的数据
all_case_data = []
for token in tokens:
for title in titles:
for tab in tabs:
for content in contents:
# 根据条件来生成对应的断言结果
if token != "e18de36f-d9ce-47e6-a2aa-1cf6508ec10b":
errmsg = "错误的accessToken"
elif len(title) == 0:
errmsg = "标题不能为空"
elif len(title) < 10:
errmsg = "标题字数太多或太少"
elif tab not in ['ask','share','dev','job']:
errmsg = '必须选择一个版块'
elif len(content) == 0:
errmsg = '内容不可为空'
# 如果数据都是正常的数据 发帖成功
else:
errmsg = None
# 每次循环,生成一条数据
casedata = (token,title,tab,content,errmsg)
# 将生成的数据放在all_case_data中
all_case_data.append(casedata)
import csv
import os
from common.file_handler import get_csvdata_dir
csvdata = get_csvdata_dir()
topicfile = os.path.join(csvdata,'topic.csv')
# 创建文件
f = open(file=topicfile, mode='w', encoding='utf8', newline="")
cw = csv.writer(f)
# 写入表头
cw.writerow(["请求地址", "请求方法", "请求头信息", "请求体", "服务器返回状态码", "服务器返回结果"])
@pytest.mark.parametrize("token,title,tab,content,errmsg",all_case_data)
def test_create_topic(token,title,tab,content,errmsg):
url = "http://47.100.175.62:3000/api/v1/topics"
bodydata = {
"accesstoken":token,
"title":title,
"tab":tab,
"content":content
}
r = requests.post(url=url,json=bodydata)
print(r.json())
# 将执行的数据,服务器返回的结果保存到文件中。
# 写入数据
cw.writerow([url,"post","",bodydata,r.status_code, r.json()])
# 添加断言 因为生成的数据包含正常场景
if errmsg is not None:
assert r.json()["error_msg"] == errmsg
运行没有问题提交代码。
创建main文件
在项目的根目录创建main文件,每次做回归测试,或者冒烟测试,批量跑接口可以从main文件直接运行。
安装pytest-html
用来生成测试报告
pip install pytest-html
添加测试报告目录
所有的测试报告文件都放在reports 目录下,在common/file_handler.py 文件中添加生成测试报告文件目录的代码。
import os
def get_root_dir():
"""
生成项目的根目录
:return:
"""
# 生成当前文件的绝对路径
p1 = os.path.abspath(__file__)
p2 = os.path.dirname(p1)
p3 = os.path.dirname(p2)
return p3
def get_csvdata_dir():
root_dir = get_root_dir()
# 路径拼接
csvdata_dir = os.path.join(root_dir,'csvdata')
# 如果路径不存在 那就创建
if not os.path.exists(csvdata_dir):
os.mkdir(csvdata_dir)
return csvdata_dir
def get_reports_dir():
root_dir = get_root_dir()
reports = os.path.join(root_dir,'reports')
if not os.path.exists(reports):
os.mkdir(reports)
return reports
if __name__ == '__main__':
root_dir = get_root_dir()
print(f"项目根目录 {root_dir}")
csvdata = get_csvdata_dir()
print(f"csvdata目录路径: {csvdata}")
report = get_reports_dir()
print(f"生成测试报告目录: {report}")
编写main文件
在main 文件中定义生成测试报告文件
import pytest
import os
import time # 日期时间模块
from common.file_handler import get_reports_dir
reports = get_reports_dir()
#转换当前时间 %Y 年 %m 月 %d 日 %H 小时 %M 分钟 %S 秒
current_time = time.strftime("%Y_%m_%d_%H_%M_%S")
# html文件路径拼接
htmlfile = os.path.join(reports,f'report_{current_time}.html')
if __name__ == '__main__':
# 通过调用pytest 内置的方法来运行所有的测试用例
pytest.main(['test_cases',
f'--html={htmlfile}',
'--self-contained-html'])
运行,可以看到结果。