脱敏后脚本

projectapi.py: 项目接口类

# -*- coding:utf-8 -*-
"""
xx项目接口类
2018-11
dinghanhua
""" import requests
import re
import pymssql #region 工具类函数
def findinfo_from_apiquerystockdetailinfo(str1,str2):
"""
从str1中找第一个"str2":...后面的值
:param str1:
:param str2:
:return: str2对应的值
"""
pattern1 = '"'+str2 + '":(.*?),"' #左右边界
result = re.search(pattern1, str1) #正则匹配
if result:
result = result.group(1).replace('"','')
return result def get_last_value_of_key(resultlist,key):
'''
从二维数组取第一行的元素对应的最后一行的值
:param resultlist:
:param key:
:return: value
'''
for i in range(0,len(resultlist[0])):
if key == resultlist[0][i]: #第一行中找到对应字段名的索引
result = resultlist[-1][i]
return result #返回数组最后一行对应的值 def round_test(data,i=0):
'''
四舍五入,解决round(7.35)=7.3的问题
:param data:
:param i: 保留的位数,默认保留一位小数
:return:
'''
if isinstance(i,int): #i是整数
raise Exception('the second param must be int')
else:
mi = 10**i
f = data*mi - int(data*mi)
if f >=0.5:
res = (int(data*mi)+1)/mi
elif f <=-0.5:
res = (int(data*mi-1))/mi
else:
res = int(data*mi)/mi
if i<=0:
res = int(res)
return res
# endregion class ProjectApi:
def api_querystockdetailinfo(self,stockcode):
"""
请求并提取股票基本信息接口数据
:param stockcode:
:return: 截取信息dict
"""
api = 'http://testdomain/querystockdetailinfo?stockcode={stockcode}'.format(stockcode = stockcode)
response = requests.get(api)
result = response.text.replace(r'\n','').replace('\\', '') # 去掉特殊字符\n,\
result_dict = {'stockcode': stockcode} #股票名称
result_dict['StockName'] = findinfo_from_apiquerystockdetailinfo(result, 'StockName') if result_dict['StockName']: #股票名称存在继续处理其他字段,否则报错并返回 # 公司概要 #剔除公司概要中“公司”“公司是”、“公司是一家”高度重复的内容
overviewvalue = result_dict['OverviewValue'] = findinfo_from_apiquerystockdetailinfo(result, 'OverviewValue') if overviewvalue.startswith('公司是一家'):
result_dict['OverviewValue'] = overviewvalue[5:]
elif overviewvalue.startswith('公司是'):
result_dict['OverviewValue'] = overviewvalue[3:]
elif overviewvalue.startswith('公司'):
result_dict['OverviewValue'] = overviewvalue[2:] if not overviewvalue.endswith('。'): #判断最后是否有句号,没有加一个
result_dict['OverviewValue'] += '。' # 市值
typecap = findinfo_from_apiquerystockdetailinfo(result, 'TypeCap')
dictcap = {'': '巨盘', '': '大盘', '': '中盘', '': '小盘', '': '微盘'}
result_dict['TypeCap'] = dictcap[typecap] # 风格
TypeStyle = result_dict['TypeStyle'] = findinfo_from_apiquerystockdetailinfo(result, 'TypeStyle')
dictstyle = {'': '成长', '': '价值', '': '周期', '': '题材', '': '高价值'}
if len(TypeStyle) == 1:
result_dict['TypeStyle'] = dictstyle[TypeStyle]
elif len(TypeStyle) >1:
typestylelist = TypeStyle.split(',') #风格可能有多个
for t in range(len(typestylelist)):
typestylelist[t] = dictstyle[typestylelist[t]]
result_dict['TypeStyle'] = '、'.join(typestylelist) # 生命周期 LifecycleValue 生命周期(单选,例:1);(1初创期、2成长期、3成熟期、4衰退期)")
LifecycleValue = findinfo_from_apiquerystockdetailinfo(result, 'LifecycleValue')
dictlifecycle = {'': '初创期', '': '成长期', '': '成熟期', '': '衰退期', '': '周期底部', '': '周期顶部', '': '周期向下', '': '周期向上'}
if LifecycleValue:
result_dict['LifecycleValue'] = dictlifecycle[LifecycleValue] # 估值 ScoreTTM 估值(分值1~5,>=4 偏低, <=2 偏高,其他适中)")
ScoreTTM = findinfo_from_apiquerystockdetailinfo(result, 'ScoreTTM')
if ScoreTTM:
if float(ScoreTTM) >= 4:
result_dict['ScoreTTM'] = '偏低'
elif ScoreTTM and float(ScoreTTM) <= 2:
result_dict['ScoreTTM'] = '偏高'
else:
result_dict['ScoreTTM'] = '适中' # 成长指数 ScoreGrowing 成长指数(分值1~5,>=4 高, <=2 低,其他中)')
ScoreGrowing = findinfo_from_apiquerystockdetailinfo(result, 'ScoreGrowing')
if ScoreGrowing:
if float(ScoreGrowing) >= 4:
result_dict['ScoreGrowing'] = '高'
elif float(ScoreGrowing) <= 2:
result_dict['ScoreGrowing'] = '低'
else:
result_dict['ScoreGrowing'] = '中'
else:
result_dict['ScoreGrowing']='' # 盈利能力
ScoreProfit = findinfo_from_apiquerystockdetailinfo(result, 'ScoreProfit') # ' ScoreProfit 盈利能力(分值1~5,>=4 高, <=2 低,其他中)' )
if ScoreProfit:
if float(ScoreProfit) >= 4:
result_dict['ScoreProfit'] = '高'
elif float(ScoreProfit) <= 2:
result_dict['ScoreProfit'] = '低'
else:
result_dict['ScoreProfit'] = '中'
else:
result_dict['ScoreProfit']='' return result_dict def api_finance(self,stockcode):
"""
请求并提取财务数据
:param stockcode:
:return: dict
"""
api = 'http://testdomain/finance?stockcode={stockcode}'.format(stockcode = stockcode)
response = requests.get(api)
response.encoding = 'utf-8-sig'
result = response.json()['data'][0]['result'] # 转化为二位数组
result_dict = {'stockcode': stockcode} #存储返回结果 if len(result) <3: #说明股票没数据
return result_dict # 当期报告期
result_dict['EndDate'] = get_last_value_of_key(result, 'EndDate')
# 预测收益报告期
ReportPeriod = get_last_value_of_key(result, 'ReportPeriod')
dictreportperoid = {'03/31': '一季度', '06/30': '上半年', '09/30': '前三季度', '12/31': '本年度'}
if ReportPeriod and ReportPeriod != result_dict['EndDate']: #预测收益报告期不等于当期报告期
ReportPeriod = get_last_value_of_key(result, 'ReportPeriod')[5:10]
result_dict['ReportPeriod'] = dictreportperoid[ReportPeriod]
else:
result_dict['ReportPeriod'] = ''
# 预测业绩情况
PerformanceType = get_last_value_of_key(result, 'PerformanceType')
result_dict['PerformanceType'] = PerformanceType
# 预测业绩比例
result_dict['PerformanceTypeRange'] = get_last_value_of_key(result, 'PerformanceTypeRange')
# 营业收入增长率
result_dict['OperatingRevenueYoY'] = get_last_value_of_key(result, 'OperatingRevenueYoY')
# 营业利润增长率
result_dict['NetProfitYoY'] = get_last_value_of_key(result, 'NetProfitYoY')
# 净资产收益率
result_dict['ROE'] = get_last_value_of_key(result, 'ROE')
# 毛利率
result_dict['SalesGrossMargin'] = get_last_value_of_key(result, 'SalesGrossMargin') return result_dict def api_quote(self,stockcode):
"""
请求并提取PETTM
:param stockcode:
:return: dict
""" api = 'http://testdomain/quote?stockcode={stockcode}'.format(stockcode=stockcode)
response = requests.get(api)
response.encoding = 'utf-8-sig'
result = response.json()['data'][0]['result'] # 转化为二位数组
result_dict = {'stockcode':stockcode}
if len(result) <3: #说明股票没数据
return result_dict
result_dict['PETTM'] = get_last_value_of_key(result, 'PE1')
return result_dict def result_of_3sourceapi(self,stockcode):
"""
拼接3个接口取出的字串
:param stockcode:
:return:
"""
result_stockdetailinfo = self.api_querystockdetailinfo(stockcode)
if result_stockdetailinfo['StockName']: #如果股票名称存在,执行后续步骤
result_finance = self.api_finance(stockcode)
result_quote = self.api_quote(stockcode) #显示三个接口结果
#print(result_stockdetailinfo)
#print(result_finance)
#print(result_quote) #空值、精度处理
OperatingRevenueYoY = round_test(float(result_finance['OperatingRevenueYoY']),1)
NetProfitYoY = round_test(float(result_finance['NetProfitYoY']),1) if result_finance['ReportPeriod'] ==''\
or result_finance['PerformanceType'] == ''\
or result_finance['PerformanceTypeRange'] == '':
ReportPeriod = PerformanceType = PerformanceTypeRange = ''
else:
ReportPeriod = ',预计' + result_finance['ReportPeriod']
PerformanceType = '业绩' + result_finance['PerformanceType']
PerformanceTypeRange = result_finance['PerformanceTypeRange'] if result_finance['ROE']:
ROE = ',净资产收益率:{0}%'.format(round_test(float(result_finance['ROE']),1))
else:
ROE = '' if result_finance['SalesGrossMargin']:
SalesGrossMargin = ',毛利率:{0}%'.format(round_test(float(result_finance['SalesGrossMargin']),1))
else:
SalesGrossMargin = '' result = '{OverviewValue} {TypeCap}{TypeStyle}股,处于{LifecycleValue}。' \
'估值{ScoreTTM},PE(TTM):{PETTM};' \
'成长性{ScoreGrowing},当期营收增长:{OperatingRevenueYoY}%,当期利润增长:{NetProfitYoY}%;' \
'盈利能力{ScoreProfit}{ROE}{SalesGrossMargin}{ReportPeriod}{PerformanceType}{PerformanceTypeRange}。'\
.format(OverviewValue = result_stockdetailinfo['OverviewValue'],
TypeCap = result_stockdetailinfo['TypeCap'],
TypeStyle = result_stockdetailinfo['TypeStyle'],
LifecycleValue = result_stockdetailinfo['LifecycleValue'],
ScoreTTM = result_stockdetailinfo['ScoreTTM'],
PETTM = round_test(float(result_quote['PETTM'])),
ScoreGrowing = result_stockdetailinfo['ScoreGrowing'],
OperatingRevenueYoY = OperatingRevenueYoY,
NetProfitYoY = NetProfitYoY,
ScoreProfit = result_stockdetailinfo['ScoreProfit'],
ROE = ROE,
SalesGrossMargin=SalesGrossMargin,
ReportPeriod = ReportPeriod,
PerformanceType = PerformanceType,
PerformanceTypeRange = PerformanceTypeRange) return result
else:
return '不存在该股票数据' def api_of_dev(self,stockcodelist,cookie,page=0,domain='testdomain.cn'):
"""
获取开发接口数据
:param 股票列表;cookie;domain默认线上
:return: 股票代码及数据
"""
headers = {'Cookie':cookie}
url = 'http://{domain}/getstockbypage?StockCodeList={stockcodelist}&PageIndex={page}&PageSize=10'.format(stockcodelist = stockcodelist,domain = domain,page=page) response = requests.get(url, headers=headers)
jsonstr = response.json()# 转成json,取出message
message = jsonstr['Message']
dict_result = {} if message:
for ele in message:
stockcode = ele['StockCode']
content = ele['Content'] # 实际结果
nickname = ele['NickName'] #发布者
if nickname == 'project000':
dict_result[stockcode] = content return dict_result def compare_vs_devapi(self,stockcodelist,cookie,page=0,domain='testdomain.cn'):
"""
开发接口数据与接口拼接数据对比
:return: bool
"""
diff_list = [] # 存储不一致的股票代码 resultofdev = self.api_of_dev(stockcodelist,cookie,page,domain) #请求开发接口
if resultofdev: #如果开发结果不为空
for stock,actual in resultofdev.items():
expected = self.result_of_3sourceapi(stock) #数据源拼接结果 '''去掉pe(ttm)对比'''
beginindex = actual.find('PE(TTM)')
endindex = actual.find(';', beginindex)
actual_result = actual[:beginindex] + actual[endindex:] beginindex = expected.find('PE(TTM)')
endindex = expected.find(';', beginindex)
expected_result = expected[:beginindex] + expected[endindex:] if actual_result != expected_result: #预期实际对比
print(stock)
print('开发:',actual_result)
print('预期:',expected_result)
diff_list.append(stock)
else:
print(stock,'一致(不含PETTM)') if diff_list: #异常股票列表不为空则输出;空则提示全部一致
print('不一致的股票列表:', diff_list)
return False
else:
print('对比结果:数据一致')
return True
else:
print('接口没有数据')
return True def compare_vs_database(self,count=10):
"""
比较数据库数据与数据源拼接字串
:param count:对比股票数量,default=10
:return:True 一致;False 不一致
""" diff_list = [] # 存储不一致的股票代码 with pymssql.connect(server='192.168.1.1', user='sa', password='sa',
database='test_db') as myconnect:
with myconnect.cursor(as_dict=True) as cursor:
cursor.execute("""SELECT top {count} StockCode,StockName,content
FROM [test_db].[dbo].[table]
where NickName ='project000' and isvalid = 1 and IsDeleted =0 order by createtime desc""".format(count=count))
for row in cursor:
stockcode = row['StockCode']
actual = row['content']
expected = self.result_of_3sourceapi(stockcode) # 数据源拼接结果 '''去掉pe(ttm)对比'''
beginindex = actual.find('PE(TTM)')
endindex = actual.find(';', beginindex)
actual_result = actual[:beginindex] + actual[endindex:]
beginindex = expected.find('PE(TTM)')
endindex = expected.find(';', beginindex)
expected_result = expected[:beginindex] + expected[endindex:] if actual_result != expected_result: # 预期实际对比
print(stockcode)
print('开发:', actual_result)
print('预期:', expected_result)
diff_list.append(stockcode)
else:
print(stockcode, '一致(不含PETTM)') if diff_list:
print('不一致的股票列表:', diff_list)
return False
else:
print('对比结果:数据全部一致')
return True

run_test.py 执行脚本:

# coding:utf-8
"""
接口测试执行脚本
""" import projectapi
import unittest class ApiTest(unittest.TestCase): def setUp(self):
self.projectapi1 = projectapi.ProjectApi() # 创建接口对象 def testcase1(self):
"""与开发接口比对"""
stockcodelist = '600000%2C600128%2C600146%2C600165%2C600186'
#通过抓包获取cookie
cookie = 'globalid=24A85DEC-AF25-36DD-C774-ED092F705767' testresult = self.projectapi1.compare_vs_devapi(stockcodelist,cookie)
self.assertTrue(testresult) def testcase2(self):
# 与数据库对比
testresult = self.projectapi1.compare_vs_database(count=10)
self.assertTrue(testresult) def testcase3(self):
"""手工查询原数据和拼接字串"""
while True:
stockcode = input('输入股票代码: ')
if stockcode.isdigit() and len(stockcode)==6 and stockcode[:2] in ('','',''):
print('数据请求中.....')
print(self.projectapi1.api_quote(stockcode))
print(self.projectapi1.api_finance(stockcode))
print(self.projectapi1.api_querystockdetailinfo(stockcode))
print(self.projectapi1.result_of_3sourceapi(stockcode))
else:
print('股票代码有误')

python接口测试-项目实践(八) 完成的接口类和执行脚本的更多相关文章

  1. python接口测试-项目实践(五) 实际结果与预期结果对比之 接口对比

    五 与开发接口对比 1 分别将三个接口封装成三个函数,传入接口参数,返回提取并处理后的各字段. 拼接字符串作为单独的函数. def api_1(code): 发送请求获取响应 提取响应数据 响应数据转 ...

  2. python接口测试-项目实践(二)获取接口响应,取值(re、json)

    一 分别请求3个接口,获取响应. 第三方接口返回有两种:1 纯字符串  2 带bom头的json字串 import requests api1 = 'url1' response1 = request ...

  3. python接口测试-项目实践(七)脚本优化

    七 脚本优化:重复代码的提取成函数:与项目接口相关的都封装到一个类中:添加手工验证脚本,增加输入值的判断逻辑 将所有与该项目接口相关的封装成类 class ProjectApi: #3个数据源接口 d ...

  4. python接口测试-项目实践(四)拼接出预期结果

    四 字符串拼接 空值处理 当某字段接口数据为空,则不显示相关字串. 比如字串原本是 "...,净资产收益率:ROE%",当接口数据中ROE为空,不显示',净资产收益率:%' 三目运 ...

  5. python接口测试-项目实践(三)数据的处理示例

    三 数据处理 枚举值的转换.如接口返回1-5,需转成对应的中文. typecap = findinfo_from_api(result, 'TypeCap') dictcap = {': '微盘'} ...

  6. python接口测试-项目实践(一) 测试需求与测试思路

    测试需求: 第三方系统提供了3个接口,需要测试前端显示的字符串里的对应数据与接口数据是否一致. 测试分层: 开发人员的设计:每周从接口取一次数据,拼接完成后保存到数据库.再从数据库取数提供接口给前端开 ...

  7. python接口测试-项目实践(六) 实际结果与预期结果对比之 数据源与数据库对比

    六 与数据库对比 import pymssql def compare_expected_vs_db(): diff_list = [] # 存储不一致的代码 with pymssql.connect ...

  8. python 全栈开发,Day21(抽象类,接口类,多态,鸭子类型)

    一.昨日复习 派生方法和派生属性 super 只有在子父类拥有同名方法的时候, 想使用子类的对象调用父类的方法时,才使用super super在类内 : super().方法名(arg1,..) 指名 ...

  9. python之路----继承的抽象类和接口类

    抽象类与接口类 接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数 ...

随机推荐

  1. vue.js 开发环境搭建

    1.安装node.js(http://www.runoob.com/nodejs/nodejs-install-setup.html) 2.基于node.js,利用淘宝npm镜像安装相关依赖 在cmd ...

  2. 每天一道剑指offer-二叉树的下一个结点

    题目 每天一道剑指offer-二叉树的下一个结点 https://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a?tpId=13& ...

  3. IE浏览器上传文件后返回结果会自动弹出下载框

    服务器使用的是node,其它语言的后台没测试过. 在IE低版本浏览器下,当你上传一个文件后后台会返回一些数据,但是IE浏览器会弹出下载提示. 这个问题是之前处理的了,没有细究,今天有人问到这个问题,顺 ...

  4. String.IsNullOrEmpty 与 String.IsNullOrWhiteSpace

    String.IsNullOrEmpty 指示指定的字符串是否为 null 或者 空字符串: 返回值:如果参数为 null 或者 空字符串("" .String.Empty),结果 ...

  5. 【转】如何用Maven创建web项目(具体步骤)

    使用eclipse插件创建一个web project 首先创建一个Maven的Project如下图 我们勾选上Create a simple project (不使用骨架) 这里的Packing 选择 ...

  6. SpringFramework中重定向

    需求: 需要在两个@Controller之间跳转,实现重定向 解决: @PostMapping("/files/{path1}") public String upload(... ...

  7. hdu 1251 统计难题 字典树第一题。

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submi ...

  8. 使用命令行编译打包运行自己的MapReduce程序 Hadoop2.6.0

    使用命令行编译打包运行自己的MapReduce程序 Hadoop2.6.0 网上的 MapReduce WordCount 教程对于如何编译 WordCount.java 几乎是一笔带过… 而有写到的 ...

  9. hadoop fs -put localfile . 时出现如下错误: could only be replicated to 0 nodes, instead of 1

    hadoop fs -put localfile . 时出现如下错误:could only be replicated to 0 nodes, instead of 1网友的说法: 这个问题是由于没有 ...

  10. Spring部分面试知识

    对Spring的理解 spring是一个轻量级的开源框架,贯穿持久层.业务逻辑层.控制层.让每一个功能模块都可以独立的分开,降低耦合度,提高代码复用度.spring通过控制反转降低耦合性,一个对象的依 ...