python——mock接口开发
mock接口开发
接口开发有很多框架,诸如 Django,flask,相比较而言,flask 是轻量级web开发框架,用来开发 mock 接口的不二之选。那你可能会问,什么叫 mock 接口呢?mock 的意思是模拟。
mock 接口的使用场景
场景1
假定在做接口测试,你正在编写自动化脚本,但是依赖于另一个接口的返回数据,但是另一个接口有问题/未开发完成,那么就需要构造接口的数据。这时候,我们可以利用 mock 接口的方式,构造出一个接口来造出我们需要的返回数据。从而不因为其他模块而阻碍当前进度。
场景2
假设公司内部部门不同,部门之间有交互,交互的话,假设别人想要我的订单表数据,但是我不想把数据库暴露给别人,那可以开发一个 mock 接口,这样他们可以通过这个接口访问数据库,但是却不知道数据库的进入方式以及数据库形式。
接下来我们就利用 flask 来构造 mock 接口,其中包括 get 请求,post 的 key-value 形式,json 形式,上传文件,访问数据库等
接口测试必备!贯通黑白盒的Mock体系实战:从抓包到代码Mock全解析
模板
首先安装好 flask 模块:pip install Flask ,其次开始写接口,接口是有模板的,具体如下:
import flask,jsonserver = flask.Flask(__name__)@server.route('/login') def welcome():data = {'code':0,'msg':'ganziwen登陆成功','session_id':'sdf234sdsdfsdfs'}return json.dumps(data,ensure_ascii=False)server.run(host='0.0.0.0',port=8888,debug=True) #5000
我们对其进行解读:
- server=flask.Flask(__name__) # 这个可以理解为固定写法,意思是起一个 flask 的服务
- @server.route('/login') # 这是个装饰器,代表下面的函数不是个普通的函数,而是一个接口,/login 是接口路径,跟下面的函数名不一定要一致,假设设置为 /,那么就是代表该接口下的默认接口
- def welcome(): # 这个是接口内的函数,要跟上一行紧挨着,且函数名不能有重复的
- return 是返回什么玩意,这里我们返回 json 串。假设要更好看点,可以在后面加:indent = 4,这样格式化起来更好看
- server.run(host='0.0.0.0',port=8888,debug=True) # 这个是代表启动接口的服务,host 可以为 127.0.0.1,这样只能本机访问,如果想要局域网内的用户都能访问,那么设置成 0.0.0.0 ,port 代表启动服务的端口,默认是 5000 ,debug=True 代表自动调试,假设改了接口代码,不用重新运行,以免忘记去重新运行启动服务
- 运行该服务,编译期内看到 * Running on http://0.0.0.0:8888/ (Press CTRL+C to quit) ;且在浏览器内访问 127.0.0.1:8888/login 查看到是我们定义的结果如下
{ code: 0, msg: "ganziwen登陆成功", session_id: "sdf234sdsdfsdfs" }
get请求
1、获取请求参数
其实上面的接口就是个 get 接口的形式,但是是相对而言比较简单的,那么我们的接口当中,get 请求有的也是要加参数的,比如说 ?username=xxxpasswd=xxx 那么怎么办呢?
@server.route('/urldata') #get请求,参数在url里面的 def urlData():u = flask.request.args.get('username')p = flask.request.args.get('password')data = {'username':u,'password':p}return json.dumps(data,ensure_ascii=False)
flask.request.args.get('key') # 这个就是接口内需要传的参数 key,前面用变量获取
这时候,访问对应的地址就应该加上这两个参数,比如:http://127.0.0.1:8888/urldata?username=123&password=456
结果
{ username: "123", password: "456" }
可以明显的看到,传的参数,显示在结果内,我们的函数要实现的功能也是如此
假设要传的参数未传,那么参数获取到的值就是 None,反映在结果内就是 Null
post请求
和 get 形式的有些许不同,在 meythods 内指定 = ['post'],默认是 get 形式的
1、form-data 形式
@server.route('/add_student',methods=['post']) def add_stu():stu_name = flask.request.values.get('name')pwd = flask.request.values.get('pwd')return json.dumps({'msg':'添加成功!'},ensure_ascii=False)
flask.request.values.get('key') # 获取 form-data 形式内的参数值
2、json形式
@server.route('/add_student2',methods=['post']) def add_stu2():if flask.request.is_json: # 判断 request 的形式是否为 json 形式,如果不加这个判断,传进来为 key-value 形式就会报错 AttributeErrorstu_name = flask.request.json.get('name')pwd = flask.request.json.get('pwd')#print(flask.request.json) # 可以打印出传进来的所有参数 return json.dumps({'msg':'添加成功!'},ensure_ascii=False)else:return json.dumps({'msg':'入参请传入json'},ensure_ascii=False)
3、上传文件
@server.route('/file',methods=['post']) def uploadFile():file = flask.request.files.get('f')print(file.filename) #获取到上传的文件名#path ='~/Desktop/'+file.filename #保存文件的路径,这个可以写成绝对路径#file.save(path) # 和上面一行是成对出现,将文件保存到绝对路径file.save(file.filename) #保存,这样是保存到 python 文件的目录下# print(dir(file))return json.dumps({'msg':'上传完成!'},ensure_ascii=False)
这个其实没有规避掉一个问题,文件名重复的怎么办?可以在文件名后面加一个时间戳,这样上传就基本不会有一样的了,这里我们就不写太多
从数据库获取数据
比如说,其他部门想要获取某个库,但是不想把整个数据库暴露给别人,怎么办呢?可以用接口实现,比如说:传一个表就给你返回这个表的数据:/table_data?table_name=xxx&limit=xxx
首先要写好 sql 的执行函数,之前我们写过:
def op_mysql(sql:str):mysql_info = {'host': '118.24.3.40','port': 3306,'password': '123456','user': 'jxz','db': 'jxz','charset': 'utf8','autocommit': True}result = '执行完成'conn = pymysql.connect(**mysql_info)cur = conn.cursor(pymysql.cursors.DictCursor) #建立游标cur.execute(sql)if sql.strip().lower().startswith('select'):# result = cur.fetchone()result = cur.fetchall()cur.close()conn.close()return result
接口内容:
import flask,json server = flask.Flask(__name__) @server.route('/table_data') def get_table_data():table = ['app_myuser','dsk_test','app_student']table_name = flask.request.args.get('table_name')limit = flask.request.args.get('limit','10') #10 为默认值if not table_name:return json.dumps({'msg':'table_name是必填字段'},ensure_ascii=False)if table_name not in table:return json.dumps({'msg':'没权限访问'},ensure_ascii=False)if limit.isdigit():sql = 'select * from %s limit %s;'%(table_name,limit)else:return json.dumps({'msg':'limit 请传入整数'},ensure_ascii=False)res = op_mysql(sql)return json.dumps(res,ensure_ascii=False)server.run(host='0.0.0.0',port = 5000,debug=True)
这个里面是写好了刚刚的要求,设定的表是可以定义的,不在里面的表访问不了,报没权限;
limit 判断了是否为整数,默认值为 10 ,也可以自己改;
table_name 必须传,没传就会报其是一个必填字段
这个是 get 实现,也可以自己定义为 post 实现
结果如下:
[ { id: 422, username: "glw", passwd: "123455", is_admin: 123, error_count: 0 }, { id: 424, username: "glw1", passwd: "123455", is_admin: 123, error_count: 0 }, …… ]
程序分目录
在正常的开发中,不可能像我们之前写的那样,什么都扔在一个文件里面,逻辑,数据,配置全部写在一起的话,难以维护。那么有没有什么框架来维护呢?接下来看一下,一个简单的框架是怎样的
API|__bin # 执行文件的目录| |__start.py||__config # 配置文件,数据库,服务器端口,md5 加盐值等| |__setting.py||__lib # 实现函数| |__interface.py # 接口实现| |__tools.py # 小工具:操作 mysql ,加盐等||__logs # 日志文件夹||__readme.txt # 文件以及文件夹内部内容说明||__第三方模块.txt # 整个工程安装运用的第三方模块说明
配置文件路径
在程序分目录的过程中,涉及到一个问题:模块之间的引用,在 windows 的 pycharm 内,可以很快地设置环境变量:在程序的主目录上,例如上述就是在 API 文件夹==>右键==>Mark Directory as ==> Source Root,设置好后,API 文件夹为蓝色,那么这个文件夹下的模块就可以相互引用。而且存在一个问题,假设 API 的同一级目录有一个:API2 也设置了 Source Root ,这样 API2 导入模块也没用,所以说 Source Root 只能有一个。
或者在启动文件内,将文件的路径写进去,这样也可以
那么,你可能会问,那我要把程序放到 Linux 内,没有 pycharm 怎么用?有没有终极解决方案呢?有的!
我们在 bin 的 start.py 里面可以设置,将 API 的路径加入到 python 环境变量内:
import sys import os BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0,BASE_PATH)
os.path.abspath(__file__) 代表本文件路径
os.path.dirname() 代表文件的父目录
也就是这里取了两级父目录,取几级视情况而定
实例
为了更好的理解目录分级,我们这里举个例子说明一下目录分级的文件:
启动文件:
start.py
配置文件
setting.py
接口:
interface.py
接口实现:
tools.py
文件说明:
readme.txt
引用模块
第三方模块.txt
这里还有个问题:安装以及引用的第三方模块需要自己写吗?不需要!我们本机所有的模块其实可以用命令导出,换了电脑也可以用命令批量安装:这个命令要在命令行做
pip freeze > 第三方模块.txt # 将安装过的模块导出到指定文件 pip install -r 第三方模块.txt # 将文件内的模块批量安装, -r 代表遍历
注意
- 重启服务的时候,切记不要把程序再运行一次,那样是重新启动了一个服务而不是重新启动之前的服务,正确做法是 Rerun 一次已经运行的服务
- 有时候点右键运行如果出现
,那么运行会报错,正常应该是
,这个是 pycharm 的问题,只要出现'xxx in xxx.py',解决办法是点击最上面的 Run,选择 Run...,再选择要运行的代码 - 只写 ip:port 不成,还等加上相应路径,否则会报 404
- 路径不能有重复的,函数名也不能有重复的
- flask.request 就是 flask 收到的所有参数,打印的类型可以再后面加比如:flask.request.json
- server.run() 只能写一个,而且只能写在最后,在这之后的代码运行不到