接口自动化测试实战1(项目接口开发)
前言
只包含后端接口,技术栈用的是:python+flask+mysql+redis
环境介绍
-
-
flask:轻量级web开发框架,快速上手
-
mysql:主流的数据库,本项目用于储存用户信息,python中可以用pymysql库操作mysql,flask中也有对应的组建
-
redis:主流的缓存数据库,本项目用于存储token,python中可以用redis库操作redis
环境准备
-
windows下安装python3.x版本
-
windows下安装pycharm
-
mysql安装(建议linux下)
-
客户端navicat安装
-
redis安装(建议linux下)
实战1:demo项目开发
开发几个涉及关联的接口
interface_demo项目需求
用户信息管理,可以注册、登录、添加用户、删除用户
注册:任何用户可以注册,对用户提交的注册信息进行校验,返回对应的信息,其中:
用户名:必填,唯一
密码:必填,只能6-12位,入库是加盐加密
真实名:必填
性别:非必填,只能0(male)或者1(female)
电话:必填,唯一,满足电话要求,数字,且11位
登录:用户登录,成功,则保存token到redis,其中:
用户名:必填
密码:必填
如果登录成功,对用户名加时间戳的字符串进行md5加密,生成的值作为token,然后将用户名作为key,token作为value,存入redis,且设置失效时间
添加用户:只能是管理员且登录成功后,才可以添加用户
token:必填,登录成功返回的token
添加者:必填
用户名:必填,唯一
真实名:必填
性别:非必填,只能0(male)或者1(female)
电话:必填,唯一,满足电话要求,数字,且11位
密码:默认是123456,不填
删除用户:只能是管理员且登录成功后,才可以删除用户
数据库表设计
navicat连接阿里云服务器mysql服务
CREATE TABLE `users`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(20) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`realname` VARCHAR(255) NOT NULL,
`sex` TINYINT(1) DEFAULT NULL,
`phone` VARCHAR(255) NOT NULL,
`utype` TINYINT(1) DEFAULT NULL,
`addtime` datetime DEFAULT NULL,
`adduser` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `phone` (`phone`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
创建成功后,查看表结构
MariaDB [katyhudson]> show tables;
+----------------------+
| Tables_in_katyhudson |
+----------------------+
| users |
+----------------------+
1 row in set (0.000 sec)
MariaDB [katyhudson]> desc users;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(20) | NO | | NULL | |
| password | varchar(255) | NO | | NULL | |
| realname | varchar(255) | NO | | NULL | |
| sex | tinyint(1) | YES | | NULL | |
| phone | varchar(255) | NO | UNI | NULL | |
| utype | tinyint(1) | YES | | NULL | |
| addtime | datetime | YES | | NULL | |
| adduser | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
9 rows in set (0.001 sec)
接口开发实现
目录规划
bin:启动目录
conf:配置
data:存放数据
lib:接口及自己写的工具
start.py
import os, sys
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# print(BASE_PATH)
sys.path.insert(0, BASE_PATH) # 将项目根路径加入环境变量
from lib.interfaces import server
from conf.settings import SERVER_PORT
if __name__ == '__main__':
server.run(host='127.0.0.1', port=SERVER_PORT, debug=True) # 启动服务
settings.py
# 服务配置
SERVER_PORT = 9999 # 服务的端口号
# mysql数据库配置
MYSQL_HOST = 'ip' # ip
MYSQL_PORT = 3306 # 端口
USER = 'root' # 用户名
PASSWORD = '123456' # 密码
DB = 'katyhudson' # 数据库名
# redis数据库配置
REDIS_HOST = MYSQL_HOST # ip
REDIS_PASSWORD = '123456' # 密码
REDIS_PORT = 6379 # 端口
EX_TIME = 6000 # token失效时间
# 其余配置
SALT = '*katyhudson*' # 加密的盐值
interfaces.py
import flask
import time
from lib.tools import conn_mysql, md5_passwd, op_redis
from flask import request, make_response # 获取请求参数
from flask import send_from_directory, jsonify
server = flask.Flask(__name__) # 把这个python文件当做一个web服务
server.config['JSON_AS_ASCII'] = False # jsonify返回的中文正常显示
# 传json
route('/register1', methods=['get', 'post']) # 第一个参数是接口的路径,第二个参数是请求方式 .
def reg():
print(request.json)
username = request.json.get("username").strip() # 必填
password = request.json.get("password").strip() # 必填
realname = request.json.get('realname', '').strip() # 必填
sex = request.json.get('sex', '0').strip() # 非必填
phone = request.json.get('phone', '').strip() # 必填
# u_type = request.json.get('type', '1')
if username and password and realname and phone:
res = conn_mysql('select username from users where username="%s";' % username)
print(res) # 如果数据库中没有请求的用户数据,返回None
if res:
if 'code' not in res:
return '{"code":9310,"msg":"用户已经存在"}'
else:
return jsonify(res)
elif len(password) < 6 or len(password) > 12:
return '{"code":9320, "msg":"密码只能6-12位"}'
elif not (sex == '0' or sex == '1'):
return '{"code":9340, "msg":"性别只能是0(male)或者1(female)"}'
elif not (phone.isdigit() and len(phone) == 11):
return '{"code":9350, "msg":"手机号格式不正确"}'
elif conn_mysql('select phone from users where phone="%s";' % phone):
return '{"code":9360, "msg":"手机号已被注册"}'
else:
password = md5_passwd(password) # 调用加密函数
sql = 'insert into users(username,password,realname,sex,phone,utype,addtime,adduser) values ("%s","%s","%s","%s","%s","%s",now(),"%s");' % (
username, password, realname, sex, phone, '1', username)
conn_mysql(sql)
return '{"code":9370,"msg":"恭喜,注册成功!"}'
else:
return '{"code":9300,"msg":"必填参数(username,password,realname,phone)都不能为空"}'
# 传k-v
route('/register2', methods=['get', 'post']) .
def reg2():
print(request.values)
username = request.values.get("username").strip() # 必填
password = request.values.get("password").strip() # 必填
realname = request.values.get('realname', '').strip() # 必填
sex = request.values.get('sex', '0').strip() # 非必填
phone = request.values.get('phone', '').strip() # 必填
# u_type = request.json.get('type', '1')
if username and password and realname and phone:
if conn_mysql('select username from users where username="%s";' % username):
return '{"code":9310,"msg":"用户已经存在"}'
elif len(password) < 6 or len(password) > 12:
return '{"code":9320, "msg":"密码只能6-12位"}'
elif not (sex == '0' or sex == '1'):
return '{"code":9340, "msg":"性别只能是0(male)或者1(female)"}'
elif not (phone.isdigit() and len(phone) == 11):
return '{"code":9350, "msg":"手机号格式不正确"}'
elif conn_mysql('select phone from users where phone="%s";' % phone):
return '{"code":9360, "msg":"手机号已被注册"}'
else:
password = md5_passwd(password)
sql = 'insert into users(username,password,realname,sex,phone,utype,addtime,adduser) values ("%s","%s","%s","%s","%s","%s",now(),"%s");' % (
username, password, realname, sex, phone, '1', username)
conn_mysql(sql)
return '{"code":9370,"msg":"恭喜,注册成功!"}'
else:
return '{"code":9300,"msg":"必填参数(username,password,realname,phone)都不能为空"}'
# 登录,可以get,也可以post
route('/login', methods=['get', 'post']) .
def login():
username = request.values.get('username', '').strip()
passwd = request.values.get('password', '').strip()
if username and passwd:
passwd = md5_passwd(passwd)
sql = 'select id,username from users where username="%s" and password="%s"' % (username, passwd)
sql_res = conn_mysql(sql) # 获取查询结果
print('sql_res:', sql_res)
if sql_res:
token_str = username + str(int(time.time())) # 用户名+时间戳
token = md5_passwd(token_str) # md5后的token
op_redis(username, token) # 放到redis中
# 下面三行,真实开发代码会这样写,这样可以在客户端浏览器设置cookie(如果是postman发请求,会在postman中设置cookie)
response = make_response('{"code":9420, "msg":"恭喜%s,登录成功","token":"%s"}' % (username, token))
response.set_cookie(username, token) # 设置cookie
return response
# return '{"code":9420, "msg":"恭喜%s,登录成功","token":"%s"}'%(username,token)
else:
# return '{"code":9410,"msg":"用户名或密码不正确"}'
# return json.dumps({"code":9410,"msg":"用户名或密码不正确"},ensure_ascii=False)
return jsonify({"code": 9410,
"msg": "用户名或密码不正确"}) # jmeter请求,中文响应乱码(需要加上server.config['JSON_AS_ASCII'] = False);postman请求,中文正常显示
else:
return '{"code":9400,"msg":"用户名和密码不能为空"}'
# 添加用户,data和token都在body中,body是k-v
route('/add_user1', methods=['post']) .
def add_user1():
token = request.values.get('token', '').strip() # 必填
adduser = request.values.get('adduser', '').strip() # 必填
username = request.values.get("username").strip() # 必填
realname = request.values.get('realname', '').strip() # 必填
sex = request.values.get('sex', '0').strip() # 非必填
phone = request.values.get('phone', '').strip() # 必填
if token and adduser and username and realname and phone:
if sex != '1' and sex != '0':
return '{"code":9340, "msg":"性别只能是0(male)或者1(female)"}'
elif not (phone.isdigit() and len(phone) == 11):
return '{"code":9350, "msg":"手机号格式不正确"}'
else:
redis_sign = op_redis(adduser) # 从redis里面取到token
if redis_sign:
if redis_sign.decode() == token:
select_sql = 'select utype from users where adduser="%s"' % adduser
res = conn_mysql(select_sql)['utype']
# print(res,type(res))
if res == 1:
return '{"code":9540,"msg":"你是普通用户,无权限添加用户"}'
else:
if conn_mysql('select username from users where username="%s";' % username):
return '{"code":9310,"msg":"用户已经存在"}'
else:
select_sql = 'select phone from users where phone="%s"' % phone
if conn_mysql(select_sql):
return '{"code":9360, "msg":"手机号已被注册"}'
else:
password = md5_passwd('123456') # 默认密码123456
sql = 'insert into users(username,password,realname,sex,phone,utype,addtime,adduser) values ("%s","%s","%s","%s","%s","%s",now(),"%s");' % (
username, password, realname, sex, phone, '1', adduser)
conn_mysql(sql)
return '{"code":9550,"msg":"添加用户成功。"}'
else:
return '{"code":9560,"msg":"token错误"}'
else:
return '{"code":9510,"msg":"未登录"}'
else:
return '{"code":9500,"msg":"必填参数(token,username,realname,phone,adduser)不能为空"}'
# 添加用户,cookie中传token,data传json
# {"username":"test6","realname":"test6","sex":"1","phone":"13800000006","adduser":"qzcsbj1"}
route('/add_user2', methods=['post']) .
def add_stu2():
token = request.cookies.get('token') # 从cookie里面获取到token
print('cookies:', request.cookies)
print('传过来的headers是:', request.headers)
print(request.json)
adduser = request.json.get('adduser', '') # 必填
username = request.json.get('username', '') # 必填
realname = request.json.get('realname', '') # 必填
sex = request.json.get('sex', '0') # 非必填
phone = request.json.get('phone', '') # 必填
if token and adduser and username and realname and phone:
if sex != '1' and sex != '0':
return '{"code":9340, "msg":"性别只能是0(male)或者1(female)"}'
elif not (phone.isdigit() and len(phone) == 11):
return '{"code":9350, "msg":"手机号格式不正确"}'
else:
redis_sign = op_redis(adduser)
if redis_sign:
if redis_sign.decode() == token:
select_sql = 'select utype from users where adduser="%s"' % adduser
res = conn_mysql(select_sql)['utype']
# print(res,type(res))
if res == 1:
return '{"code":9540,"msg":"你是普通用户,无权限添加用户"}'
else:
if conn_mysql('select username from users where username="%s";' % username):
return '{"code":9310,"msg":"用户已经存在"}'
else:
select_sql = 'select phone from users where phone="%s"' % phone
if conn_mysql(select_sql):
return '{"code":9360, "msg":"手机号已被注册"}'
else:
password = md5_passwd('123456')
sql = 'insert into users(username,password,realname,sex,phone,utype,addtime,adduser) values ("%s","%s","%s","%s","%s","%s",now(),"%s");' % (
username, password, realname, sex, phone, '1', adduser)
conn_mysql(sql)
return '{"code":9550,"msg":"添加用户成功。"}'
else:
return '{"code":9560,"msg":"token错误"}'
else:
return '{"code":9510,"msg":"未登录"}'
else:
return '{"code":9500,"msg":"必填参数(token,username,realname,phone,adduser)不能为空"}'
# 添加用户,cookie中传token,data传json,添加的用户信息在嵌套字典中
# {"adduser":"qzcsbj","data":{"username":"test9","realname":"test9","sex":"1","phone":"13800000009"}}
route('/add_user3', methods=['post']) .
def add_stu3():
token = request.cookies.get('token')
print('cookies:', request.cookies)
print('传过来的headers是:', request.headers)
print(request.json)
data = request.json.get('data')
print(data, type(data))
adduser = request.json.get('adduser', '') # 必填
username = request.json.get('data').get('username', '') # 必填
realname = request.json.get('data').get('realname', '') # 必填
sex = request.json.get('data').get('sex', '0') # 非必填
phone = request.json.get('data').get('phone', '') # 必填
print(adduser, username, realname, sex, phone)
if token and adduser and username and realname and phone:
if sex != '1' and sex != '0':
return '{"code":9340, "msg":"性别只能是0(male)或者1(female)"}'
elif not (phone.isdigit() and len(phone) == 11):
return '{"code":9350, "msg":"手机号格式不正确"}'
else:
redis_sign = op_redis(adduser)
if redis_sign:
if redis_sign.decode() == token:
select_sql = 'select utype from users where adduser="%s"' % adduser
res = conn_mysql(select_sql)['utype']
# print(res,type(res))
if res == 1:
return '{"code":9540,"msg":"你是普通用户,无权限添加用户"}'
else:
if conn_mysql('select username from users where username="%s";' % username):
return '{"code":9310,"msg":"用户已经存在"}'
else:
select_sql = 'select phone from users where phone="%s"' % phone
if conn_mysql(select_sql):
return '{"code":9360, "msg":"手机号已被注册"}'
else:
password = md5_passwd('123456')
sql = 'insert into users(username,password,realname,sex,phone,utype,addtime,adduser) values ("%s","%s","%s","%s","%s","%s",now(),"%s");' % (
username, password, realname, sex, phone, '1', adduser)
conn_mysql(sql)
return '{"code":9550,"msg":"添加用户成功。"}'
else:
return '{"code":9560,"msg":"token错误"}'
else:
return '{"code":9510,"msg":"未登录"}'
else:
return '{"code":9500,"msg":"必填参数(token,username,realname,phone,adduser)不能为空"}'
# 添加用户,data和token都在body中,body是json
# {"token":"dcf6455d20a8cb01b573e0838cb79e7a","username":"test6","realname":"test6","sex":"1","phone":"13800000006","adduser":"qzcsbj"}
route('/add_user4', methods=['post']) .
def add_stu4():
token = request.json.get('token') # 从cookie里面获取到token
adduser = request.json.get('adduser', '') # 必填
username = request.json.get('username', '') # 必填
realname = request.json.get('realname', '') # 必填
sex = request.json.get('sex', '0') # 非必填
phone = request.json.get('phone', '') # 必填
if token and adduser and username and realname and phone:
if sex != '1' and sex != '0':
return '{"code":9340, "msg":"性别只能是0(male)或者1(female)"}'
elif not (phone.isdigit() and len(phone) == 11):
return '{"code":9350, "msg":"手机号格式不正确"}'
else:
redis_sign = op_redis(adduser)
if redis_sign:
if redis_sign.decode() == token:
select_sql = 'select utype from users where adduser="%s"' % adduser
res = conn_mysql(select_sql)['utype']
# print(res,type(res))
if res == 1:
return '{"code":9540,"msg":"你是普通用户,无权限添加用户"}'
else:
if conn_mysql('select username from users where username="%s";' % username):
return '{"code":9310,"msg":"用户已经存在"}'
else:
select_sql = 'select phone from users where phone="%s"' % phone
if conn_mysql(select_sql):
return '{"code":9360, "msg":"手机号已被注册"}'
else:
password = md5_passwd('123456')
sql = 'insert into users(username,password,realname,sex,phone,utype,addtime,adduser) values ("%s","%s","%s","%s","%s","%s",now(),"%s");' % (
username, password, realname, sex, phone, '1', adduser)
conn_mysql(sql)
return '{"code":9550,"msg":"添加用户成功。"}'
else:
return '{"code":9560,"msg":"token错误"}'
else:
return '{"code":9510,"msg":"未登录"}'
else:
return '{"code":9500,"msg":"必填参数(token,username,realname,phone,adduser)不能为空"}'
# 删除用户
tools.py
from conf.settings import *
# 加盐加密
def md5_passwd(str):
str = str + SALT # 加盐
import hashlib
md = hashlib.md5() # 构造一个md5对象
md.update(str.encode())
res = md.hexdigest()
return res
# 操作数据库
def conn_mysql(sql):
import pymysql
try:
conn = pymysql.connect(host=MYSQL_HOST, user=USER, password=PASSWORD, db=DB, charset='utf8', port=MYSQL_PORT)
except Exception as e:
print('mysql连接出错,错误信息为%s' % e)
res = {"code": 9901, "msg": 'mysql连接出错,错误信息为%s' % e}
else:
cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
try:
cur.execute(sql)
except Exception as e:
msg = "sql执行出错,请检查sql,错误信息为:%s" % e
res = {"code": 9902, "msg": msg}
else:
res = cur.fetchone()
conn.commit()
finally:
cur.close()
conn.close()
print("res:", res)
return res
# 操作redis
def op_redis(k, v=None):
import redis
r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD)
if v: # v是真,表明是set,否则是get
r.setex(k, EX_TIME, v) # 时间是秒,等同于set name jack ex 10
else:
return r.get(k)