作为一枚中台测试,我们的功能太过依赖上游数据,比如我只需要测试支付,但必须要一步步填写各种资料提交订单才可以测支付功能,此类数据我们可以通过python的request、json模块模拟创建订单过程,实现一秒创建订单。
梳理完成该功能步骤:
1.学习了解request、json模块,抓取请求,封装为函数,参数处理、发送请求、接收处理返回结果
2.Django多数据联用,复杂功能依赖多个数据库
4.页面模拟请求、校验返回结果、返回关键信息到页面
一、学习了解request、json模块
1.1 抓取请求
使用charles或者fiddler抓取创建订单请求,保存请求、参数列表、返回结果
1.2 处理参数列表为python字典格式
使用vim替换功能格式化列表为python字典的格式
1.3 封装为函数,模拟发送请求、接收响应结果
使用request模拟客户端提交订单请求,请求参数、接收返回结果并转换为json格式返回
代码示例:
# coding:utf-8
import requests
import json
'''
author:xxx
功能:
1.创建xx订单
。。。
创建日期:xxxx-xx-xx
修改日期:xxxx-xx-xx by xxx
'''
# 提交xx订单
def commitOrder(userId, xxId, providerId, productId, compactId, token):
url = 'http://mobile-api.xxx.com/xxxapi/xx_xx?ck=16840f92-1f83-4a68-8a39-e6262f6f11e5'
data = {
......(非关键参数直接给定义值即可)
'xxx': str(xxId),
......
'xxx': str(ProductId),
'xxx': str(userid),
......
'xxx': str(providerId),
......
'xxx': str(compactId),
......
'xxx': str(userid),
......
'_t': token,
......
'xxx': '[{"id":"0","xxx":"autoCreateOrder"}]'
}
res_1 = requests.post(url, data).json()
res_2 = json.dumps(res_1, indent=2)
res = json.loads(res_2)
content = res.get('content')
return content
二、Django多数据联用
该功能参考了尚学堂以为老师的攻略,整个分享写的非常非常详细:
https://code.ziqiangxuetang.com/django/django-tutorial.html
2.1在项目中创建database_router.py添加如下代码
# -*- coding: utf-8 -*-
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
class DatabaseAppsRouter(object):
"""
A router to control all database operations on models for different
databases.
In case an app is not set in settings.DATABASE_APPS_MAPPING, the router will fallback to the `default` database.
Settings example:
DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
"""
def db_for_read(self, model, **hints):
""""Point all read operations to the specific database."""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **hints):
"""Point all write operations to the specific database."""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **hints):
"""Allow any relation between apps that use the same database."""
db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
if db_obj1 and db_obj2:
if db_obj1 == db_obj2:
return True
else:
return False
return None
# for Django 1.4 - Django 1.6
def allow_syncdb(self, db, model):
"""Make sure that apps only appear in the related database."""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(model._meta.app_label) == db
elif model._meta.app_label in DATABASE_MAPPING:
return False
return None
# Django 1.7 - Django 1.11
def allow_migrate(self, db, app_label, model_name=None, **hints):
print(db, app_label, model_name, hints)
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(app_label) == db
elif app_label in DATABASE_MAPPING:
return False
return None
2.2 在setting.py中添加app、数据库、app-db对应关系
INSTALLED_APPS添加应用:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
'account',
'contract',
'order',
......
]
DATABASES添加数据库:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBname1',
'USER': 'username',
'PASSWORD': 'pwd',
'HOST': 'ip地址',
'PORT': '3306',
},
'users': {
'ENGINE':'django.db.backends.mysql',
'NAME': 'DBname2',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
'contract': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBname3',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
'order': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'DBname4',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
'account': {
'ENGINE':'django.db.backends.mysql',
'NAME': 'DBname4',
'HOST': 'ip地址',
'USER': 'username',
'PASSWORD': 'pwd',
'PORT': '3306'
},
......
}
2.3 定义常量
DATABASE_ROUTERS = ['tools.database_router.DatabaseAppsRouter']
2.4定义DATABASE_APPS_MAPPING:
DATABASE_APPS_MAPPING = {
#'app_name':'database_name',
'user': 'users',
'contract':'contract',
'order':'order',
'account':'account',
'xxapp':'xxDB',
......
}
2.5 在应用模型类中添加如下代码:
app_label = 'users'
2.6 操作数据库代码:
userId = User.objects.using('users').get(username=userName).fld_userid
三、页面设计
左侧点击创建订单,右侧显示创建订单页面
创建订单页面需要用户名、服务提供者信息、服务类型下拉列表(bootstrap下拉列表控件)等信息
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>bootstrap 搜索下拉框</title>
<!-- jquery -->
<script src="http://cdn.staticfile.org/jquery/2.1.1/jquery.min.js" type="text/javascript"></script>
<!-- bootstrap -->
<link href="http://cdn.staticfile.org/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.staticfile.org/twitter-bootstrap/3.3.1/js/bootstrap.min.js" type="text/javascript"></script>
<!-- bootstrap-select -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/css/bootstrap-select.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/js/bootstrap-select.min.js"></script>
<style>
table {
width: 60%;
border: 0px;
{#mso-cellspacing:1px ;#}
cellpadding:"4";
background-color: azure;
align-content: center;
}
.title{
width: 25%;
}
{#td{#}
{# width: 100%;#}
{#}#}
input{
width: 100%;
background-color: azure;
}
select,option{
width: 300px;
}
</style>
</head>
<body>
<h1>创建订单页面</h1>
<form method="POST" action="createorder_action">
<table border="1" >
<tr>
<td class="title">用户名</td>
<td><input type="text" name="userName" placeholder="请输入xx用户名"></td>
</tr>
<tr>
<td class="title">新建xx名称</td>
<td><input type="text" name="patientName" placeholder="新建xx名"></td>
</tr>
<tr>
<td class="title">服务提供者用户名</td>
<td><input type="text" name="docotorName" placeholder="请输入服务提供者用户名"></td>
</tr>
<tr>
<td class="title">服务类型</td>
<td>
<select name='servicetype' class="selectpicker show-tick form-control" data-live-search="true" width="50%">
<option value="1">服务1(fuwu1)</option>
<option value="2">服务2(fuwu2)</option>
<option value="3">服务4(fuwu3)</option>
<option value="4">服务5(fuwu4)</option>
<option value="5">服务5(fuwu5)</option>
<option value="6">服务6(fuwu6)</option>
</select>
</td>
</tr>
<tr>
<td>1分钱订单(仅支持xx服务功能)</td>
<td>是<input type="radio" name="is_one_money" value="1" style="width:30px" />
否<input type="radio" name="is_one_money" value="0" checked="checked" style="width:30px"/>
</td>
</tr>
<tr>
<td colspan="2" align="center"><button type="submit">创建订单</button></td>
<td></td>
</tr>
</table>
</form>
<div><h2>{{ message }}</h2></div>
</body>
</html>
四、处理函数
涉及公司内部信息较多,截取部分代码
view.py编写处理函数
def createorder(request):
response = render(request, "createorder.html")
return response
def createorder_action(request):
'''
获取用户输入
'''
userName = request.POST.get("userName")
xxName = request.POST.get("xxName")
providerName = request.POST.get("providerName")
servicetype = request.POST.get('servicetype')
is_one_money = request.POST.get("is_one_money")
try:
'''
根据用户输入获取请求需要的信息,
这里一般需要自己在应用中封装一些公用的方法/定义Const常量文件等引入当前文件方便使用。
比如封装函数根据输入的用户名获取用户id,
定义Const常量文件根据用户选择的服务匹配对应常量值以避免重复写或手写出错
'''
utoken = gettoken(userName)
userId = User.objects.using('users').get(username=userName).fld_userid
docId = getDocId(docName)
xxId1 = getxxId1(xxId1)
xxId2 = createNewPatient(userId, xxName, utoken)
providerId = getProviderId(xxId1)
orderinfo = ''
response = ''
if servicetype == '1':
compactinfo = getCompactInfo(ProviderId, contract.ServicedefTypeConst.type1)
compactId = compactinfo.id
productId = compactinfo.productid
orderinfo = commitNetCase(userId, xxId2, providerId, productId, compactId, utoken)
# message=userId, patientId, doctorteamHotId, basicProductId, compactId, utoken
message = 'xx类型订单提交成功,订单id为:' + str(orderinfo.get('orderId'))
response = render(request, 'createorder.html', {"message": message})
elif servicetype == '2':
......
elif servicetype == '3':
......
elif servicetype == '4':
......
elif servicetype == '6':
......
if is_one_money == '1':
if servicetype == '1' or servicetype == '2' or servicetype == '5' or servicetype == '6':
update_to_one_point(orderinfo.get("orderId"))
elif servicetype == '3':
update_to_one_point(orderinfo.get("xxx1"))
elif servicetype == '4':
update_to_one_point(orderinfo.get("xxx2"))
elif is_one_money == '0':
pass
else:
raise Exception("错误的是否造1分钱订单状态")
return response
except InsecureRequestWarning:
if is_one_money == 1:
if servicetype == 1 or servicetype == 2 or servicetype == 5 or servicetype == 6:
update_to_one_point(orderinfo.get("orderId"))
elif servicetype == 3:
update_to_one_point(orderinfo.get("xxx"))
elif servicetype == 4:
update_to_one_point(orderinfo.get("xxx"))
elif is_one_money == 0:
pass
else:
raise Exception("错误的是否造1分钱订单状态")
return response
except Exception as e:
message = '输入信息有误,或医生不存在该服务!'
response = render(request, 'createorder.html', {"message": message})
return response
五、配置路由
项目路由配置:
urlpatterns = [
# path('admin/', admin.site.urls),
path("index/",views.index),
......
path("order/", include("order.urls")),
......
]
应用路由配置:
urlpatterns = [
path("createorder",unifiedorder.views.createorder),
path(r"createorder_action", unifiedorder.views.createorder_action),
]
六、大功告成,可以一键提交订单啦
图片.png七、完善异常处理
进行调试、处理异常情况等,让用户输入异常时页面给出对应提示
网友评论