• [问题求助] 【ABC】【智慧园区自动化测试工程】导入智慧园区自动化测试工程,无法访问git库
    【功能模块】导入智慧园区自动化测试工程,无法访问git库【操作步骤&问题现象】【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 嵌入软件测试工具
    专家winAMS获得机能安全标百准ISO26262/IEC61508工具认证,是日本工业制造度领域普遍使用的针对C/C++的单元/集成测试工具.winAMS将通过交叉编译生成的原始代码作为评价代码,具有使用芯片仿真器进行仿真功能的测试工具.不仅可以对C/C++语言编写的程序进行逻辑水平的测试,还可以对嵌入式软件特有的依存于芯片的问题点进行确认.
  • [公告] 【获奖名单已发布---DevOps实战集训营】学习任务打卡贴
    受疫情影响,北京市、石家庄市、邢台市等部分网点无法收寄快递,您的快递可能无法准时到货,给您带来不便敬请谅解。获奖名单及积分公布获奖人员1月13日24:00前在此链接>>>>>反馈您的收货地址:奖项排名昵称完成课堂作业得分完成《软件开发平台DevCloud介绍及实战》课堂进度完成《7天玩转性能&接口测试实战营》课堂进度完成《黑白棋实时对战游戏开发沙箱实验》合计得分奖项特等奖1蜡笔不辣1422523荣耀猎人战甲背包(黑色)一等奖2AAAI1422523HUAWEI   HiLink力博得智能声波牙刷一等奖3帅气的我1422523HUAWEI   HiLink力博得智能声波牙刷一等奖4Archive1422523HUAWEI   HiLink力博得智能声波牙刷一等奖5谭涟漪1422523HUAWEI   HiLink力博得智能声波牙刷二等奖6ruochen1422523荣耀手环5i二等奖7孙小北1422523荣耀手环5i二等奖8虚荣Vainglory1422523荣耀手环5i二等奖9阿弥陀佛1322522荣耀手环5i二等奖10实柏1222521荣耀手环5i二等奖11起名字什么的最烦了142218荣耀手环5i二等奖12我就灬是我142218荣耀手环5i二等奖13付豪之家02259荣耀手环5i二等奖14qweqwe02259荣耀手环5i二等奖15abcabc02259荣耀手环5i二等奖16Aiphone02259荣耀手环5i二等奖17PERFECT02259荣耀手环5i二等奖18狐妖02259荣耀手环5i二等奖19tanboss3234228荣耀手环5i二等奖20spike2226荣耀手环5i二等奖21Hello   Digger0224荣耀手环5i二等奖22旧时光里的温柔0224荣耀手环5i二等奖23爱浩吃的鱼0224荣耀手环5i二等奖24zdnyyh0224荣耀手环5i二等奖25橙子是红的224荣耀手环5i三等奖26外围的小尘埃0224荣耀移动电源2三等奖27平平无奇的平平0224荣耀移动电源2三等奖28power110224荣耀移动电源2三等奖29红鲤鱼与绿鲤鱼0224荣耀移动电源2三等奖30慕雪0224荣耀移动电源2三等奖31一只无脑程序员0224荣耀移动电源2三等奖32shaoqunshao0224荣耀移动电源2三等奖33大豆0224荣耀移动电源2三等奖34老老刘224荣耀移动电源2三等奖35感恩你我他022荣耀移动电源2三等奖36大脑斧022荣耀移动电源2三等奖37psfaaa022荣耀移动电源2说明:华为云账号hw98****3 和hw13****3存在相互作弊行为,取消本次排名资格。完成实战集训营学习,参与回帖打卡,享海量学习礼!01  课程介绍《软件开发平台DevCloud介绍及实战》  课程简介本课程主要内容包括:软件发展历史及趋势,华为敏捷开发、DevOps实践。  课程目标通过本课程学习,使学员:1、熟悉软件行业趋势挑战及软件开发云解决之道;2、熟悉敏捷开发DevOps知识体系;3、通过应用开发场景,熟悉软件开发云产品功能。课程大纲   第1章 软件开发云概览   第2章 软件开发平台 DevCloud详述《7天玩转性能&接口测试实战营》及实操课程简介:本期课程依托华为云DevCloud,以实践操作为课程基础,使学员掌握DevOps持续自动化测试、接口测试、单接口测试、多接口组合场景、网站性能测试、CI/CD持续自动化测试等能力。体验性能和接口测试的整体流程,汲取自动化测试的核心知识。课程大纲:第1章 Day1 DevOps持续自动化测试概述第2章 Day2 接口测试基本原理第3章 Day3 设计单接口测试第4章 Day4 设计多接口组合场景测试第5章 Day5 性能测试基本原理第6章 Day6 性能测试策略和指标分析第7章 Day7 测试金字塔原理和CI/CD持续自动化测试《黑白棋沙箱实验》通过动手体验,学习基于华为软件开发服务DevCloud构建部署黑白棋游戏应用。使用软件开发服务DevCloud实现代码仓库管理,使用软件开发服务DevCloud实现编译、构建、部署。02 学习任务&奖励要求【学习完成时间】 2020.12.5 -2020.12.31【学习任务】参与训练营,必须先开通DevCloud套餐获取实操资源!!点击直达DevCloud基础套餐订购地址>>>序号任务详情活动积分奖励课程入口1完成《软件开发平台DevCloud介绍及实战》课堂进度任务完成方式:在本帖中,回复自学课程完成进度回复格式:课程名称+华为云ID+课程完成截图(露出右上角华为云ID)见附录截图1要求2积分点击前往2完成《7天玩转性能&接口测试实战营》课堂进度任务完成方式:在本帖中,回复自学课程完成进度回复格式:课程名称+华为云ID+课程完成截图(露出右上角华为云ID)见附录截图1要求2积分        点击前往3完成课堂作业任务完成方式:在classroom课堂提交7天作业,每天作业由教师判断,判定有效则当次作业可获得2积分。12月29日后DAY3 URL访问有问题,请直接按照用例执行结果截图,教师会判定为有效。2积分/每次作业(最高14积分)点击前往4完成《黑白棋实时对战游戏开发沙箱实验》任务完成方式:在本帖中,回复沙箱实验完成进度回复格式:华为云ID+沙箱完成截图每日名额有限,请尽早完成。见附录截图2要求。实验环境已解决,可以启动实验。5积分       点击前往【奖励规则】积分>=2即可参与排名抽奖,根据用户最终评分排名,获得奖品。     积分排名第一名,可获得荣耀猎人战甲背包(黑色)1份。   积分排名2-5名,可获得HUAWEI HiLink力博得智能声波牙刷1个积分排名第6-25名,可获得荣耀手环5i 1个。积分排名第26-77名,可获得荣耀移动电源2 1000mAh (Max 18W) 快充版(白色)Micro版一台(限量50个)。   【附录】截图1要求:显示学习进度为100%,且右上角显示清晰的个人华为云账号。如示例。截图2要求:要求显示实验进度为100%,如示例。 【声明】1、获取学习奖品必须开通DevCloud基础套餐或是付费套餐,才可获得评奖资格。2、后台可查每个华为云账号的学习进度和沙箱实验进度,如有造假,取消排名资格。3、如有积分相同、排名并列的情况,通过随机抽取方式决定排名。4、活动解释权归华为云所有。【相关链接】1、0元订购DevCloud基础套餐,反馈订单截图,参与抽奖>>>2、邀请好友订购DevCloud基础套餐,好友反馈订单截图,参与抽奖,邀请人参与邀请好友排名奖。直达链接>>>>>3、推荐小助手获取分享海报,分享海报,邀请好友赢取新年红包。
  • [热门活动] 已结束【答题赢京东卡等大礼】华为云DevCloud云测赋能培训 | 0代码门槛完成自动化测试脚本实践
    圣诞快乐 中奖名单公布 请获奖者填写领奖信息>>https://devcloud.huaweicloud.com/expert/open-assessment/qtn?id=d7322fc329f5499380fba33ca93b72f4&utm_source=yunce(发放奖品以获奖名单中的华为云账号为准,请用获奖华为云账号登录本问卷,确保信息一致。)注:领奖信息收集截止至12月31日8点,超过时间未填写将视为弃奖。奖品将于元旦后发放。—— 活动结束 ——活动时间:12月9日-23日活动规则:在活动时间内,结合给定试题,按照操作指导完成答题,并将“有效的答题截图+华为云账号”回复到本帖留言区,华为云技术专家对提交的截图进行检验,如通过则回复“答题通过”,获得“答题通过”回复的即为有效答题回帖用户,可参与抽奖。(下文附操作指导及有效答题截图示例,请认真查看,按照指导进行操作即可完成,so easy~)抽奖规则:12月25日主办方用有效答题回帖用户提供的华为云账号进行抽奖,中奖概率为有效答题回帖人数的40%,且总中奖名额不超过60人。中奖名单将在论坛公布。注意:l   每个用户可重复提交回帖,但只享有一次抽奖机会,不可重复获奖。每个论坛用户对应回帖中提交的华为云账号统一,且与截图中的保持一致,否则视为无效。 l   有效答题回帖指截图是按照示例要求上传的、且通过华为云技术专家检验合格回复“答题通过”的回帖。需要上传2张图片:一张是显示“成功”的操作结果,一张是附带华为云账号的执行页面(示例见版主留言回帖),l   操作过程中有疑问也可在留言区提问交流,提问帖和交流帖不参与抽奖。 —— 奖品包括——HUAWEI Sound 智能音箱(帝瓦雷四喇叭 40W低音炮)(1个,当获奖人数≥20人时发放)、荣耀xSport运动蓝牙耳机、100元京东卡、50元京东卡、荣耀体脂称2、AM115半入式耳机,以上奖品随机发放(每样奖品的数量将按照实际参与抽奖人数进行分配,具体以实际发放为准),颜色不可选。(往下看操作指导还有大奖彩蛋~)抽奖时间:12月25日,中奖名单及兑奖地址将在论坛公布,请收藏本帖以免错过哦~奖品将于活动结束后7-30天内发放。 (以上奖品图片供参考,具体以实际发放为准) 彩蛋1:观看直播《HCIP职业认证专场——DevOps持续测试与反馈》还可以获得直播抽奖>>https://bbs.huaweicloud.com/live/DevRun_live/202012162000.html—— 试题:订单系统接口测试自动化实操 —— 本实例是为了测试一个订单系统微服务的登录API接口,类似:您打开XX网站,输入用户名和密码,然后后台服务如何能知道您的用户名和密码是正确的,那么通过本实例您就很容易的了解了后台的登录API接口是如何工作的,写一个简单的接口测试用例其实很简单。 —— 操作指导 ——前期准备1、  完成账号实名认证>>点击实名认证链接:https://account.huaweicloud.com/usercenter/?region=cn-north-1&locale=zh-cn#/accountindex/realNameAuthing 2、 开通DevCloud基础版打开https://devcloud.huaweicloud.com/cloudtest/home,登录自己的华为云账号或者注册一个华为云账号,选择切换地域;按照弹窗提示开通DevCloud基础版(免费,不需要选择支付方式,直接按照步骤点击即可完成开通)彩蛋2!!!开通后截图返回这里,还有很多机会得HUAWEI MatePad大奖哦>>https://bbs.huaweicloud.com/forum/thread-90536-1-1.html一、新建测试项目1、刷新页面或进入https://devcloud.huaweicloud.com/cloudtest/home,新建一个scrum项目,项目名称自己定义。2、新建接口测试用例新建后点击“测试设计->接口测试->新建”,新建一个接口测试用例,输入用例名称后,点击“保存并编写脚本”。 二、设置环境参数设置hostIP,由于IP和端口涉及敏感参数,所以选择加密。hostIP地址是:114.115.173.182:84431、点击按钮弹出环境参数页面2、录入参数名:hostIP;值:114.115.173.182:8443如果需要加密,勾选【敏感参数】;不需要加密,则无需勾选。三、录入URL,登录系统1、录入URL和请求体请求信息设置,点击URL请求设置请求方法。点击请求方法下拉列表,将GET改成POST;设置请求协议,点击网络协议下拉列表,将https://改成http://;填写URL。输入入登录接口的URL:$${hostIP}/cloudtest-demo/v1/login,IP和端口使用已定义的环境参数代替;设置请求参体:点击【+JSON请求体】按钮,录入请求体:{   "name":"张三"   “pwd”:"123456" }2、录入检查点点击“检查点”,选择响应码,目标值为200。点击保存。3、点击调试检查结果点击调试按钮,检查响应是否符合预期,符合条件则用例执行成功,不符合条件则用例执行失败。显示成功,及表示后台测试验证通过,账户登录成功。操作完成。四、需要上传的截图包括(示例)图一,点击“结果记录”,找到位置。展示自己的用例执行的结果记录以及对应的日志,获取调试成功的截图。(需要返回的答题截图1)图二,刷新页面,选择“测试执行”,找到页面。展示自己的接口用例名称以及自己的用例执行结果(此截图将附带操作者的华为云账号,证明本人操作)(需要返回的答题截图2) *活动解释权归主办方所有。补充说明a)      本活动需要用户按照要求完成操作,同一电话号码注册的账号(包括兑奖时提供的电话号码相同者)视为同一个用户,使用非正常途径或手段(如写脚本刷接口等方式)获得的奖励无效,且一旦发现作弊行为,华为云开发者社区将取消对应人员的获奖资格、冻结违规账号,已兑换的礼品也不予发放。b)      中奖名单已本帖公布,请获奖者填写领奖信息>>https://devcloud.huaweicloud.com/expert/open-assessment/qtn?id=d7322fc329f5499380fba33ca93b72f4&utm_source=yunce(发放奖品以获奖名单中的华为云账号为准,请用获奖华为云账号登录本问卷,确保信息一致。)注:领奖信息收集截止至12月31日8点,超过时间未填写将视为弃奖。奖品将于元旦后发放。抽奖截图
  • [介绍/入门] 探索云性能测试服务如何和助力11·11购物狂欢节
    探索云性能测试服务如何和助力11·11购物狂欢节       刚刚结束的双11全球狂欢季,当日总成交额达到了4982亿人民币,实时物流订单量破22.5亿单!大家有没有加入“剁手”大军,感受这场购物狂欢呢?活动期间,多个购物平台都热闹非凡,随着科技的发展,我们的消费体验一年比一年丝滑,有没有经历过好不容易抢到秒杀却无法刷出支付页面的焦灼?有没有为了抢购首发新品一直刷新页面的经历?为何今年体验如此丝滑呢?原因是得益于云计算的发展,各电商平台对自己的负载能力提前进行了测试和分析,于是越来越了解平台性能,在活动前就已经做好了升级准备,那究竟是通过什么神器帮助企业了解到这些信息的呢?今天我们就来探个究竟。       以前,对于平台的负载能力的测试和判断,无论测试工具搭建的时间成本,还是采购成本都比较高的,而且只能对单个或少数软件进行测试,所购买的测试设备无法随着平台业务发展的需要而提升测试能力,由此一来,都很难精准地掌握其负载能力,监控性能问题,直接影响到了我们是否能够丝滑完成付款,抢购到种草已久的东西,导致用户的流失。       随着云计算技术的飞速发展,就出现了云性能测试服务,相较于传统的性能测试工具,云性能测试服务独有的特点之一就是如云般轻量化,即开即用,无需体验预留或采购资源,使得测试工作变得极为方便。云性能测试服务的测试能力更可以根据业务测试需要随时扩展,比传统软件更灵活地支持更高并发的模拟测试。这正是云性能测试服务成为当今性能测试界“网红”的原因。       2017年12月15日,秉持着高性能、低成本、可扩展和支持复杂场景等理念,“网红”华为云云性能测试服务(CPTS)C位出道,为云性能测试服务带来更多精彩体验。云性能测试服务(Cloud Performance Test Service,简称CPTS)是一项为基于HTTP/HTTPS/TCP/UDP/WEBSOCKET等协议构建的云应用提供性能测试的服务,具备强大的分布式压测能力。CPTS支持快速模拟大规模并发用户的真实业务高峰场景,可以很好的支持报文内容和时序自定义、多事务组合的复杂场景测试。接下来,我们对其特点一一进行了解。    灵活自定义,HTTP/HTTPS协议测试场景支持       通过支持标准、开放的HTTP/HTTPS报文可根据自身需要进行快捷的自定义,简单调整即可给不同测试应用发送压力测试流量。也可以根据测试对象的实际需求,对HTTP/HTTPS协议报文的任何字段内容进行自定义,包括HTTP方法、URL、Header和Body等字段的设置和编辑内容。对于检测结果,可以进行自定义响应结果校验,针对不同的请求,可支持用户配置检查点,在获取到响应报文后针对响应码及头域内容做结果检验,只有条件匹配后才可认定是正常响应。测试任务模型自定义,支持复杂场景测试       通过多种事务元素与测试任务阶段的灵活组合,可以帮助用户测试多操作的并发场景下的应用性能表现。而所创建的事务可以被多个测试任务复用,针对每个事务可以定义多个测试阶段,并对每个阶段分别定义持续时间和并发用户数,模拟流量起伏的复杂场景。专业性能测试报告,应用性能表现一目了然       可提供事务TPS、并发用户、响应时延、事务累计、结果校验失败、响应超时等多种细分维度统计能力。还提供了实时和离线两种类型的测试报告,我们可以随时查看和分析测试数据。按需使用测试集群,私有压测集群管理       不同于传统测试工具,华为云云性能测试服务可以按需创建测试集群,并实现租户间流量隔离,测试期间更可以实施扩容、缩容测试集群,升级测试能力。完成测试后可随时删除集群。云计算日益发展的今天,平台、应用是否能及时优化性能,适应自身发展尤为重要,看完本文介绍,是否想亲自体验CPTS服务,探索更多功能呢?欢迎体验沙箱实验《基于CloudTest对云端应用进行性能测试》,实验中可一键预置好弹性云服务器ECS和虚拟私有云VPC等所需服务,让我们更专注于云性能测试服务的体验。更提供了详细的实验手册,每一步操作都有详细指导,有的放矢,轻松体验如何创建测试工程、添加测试任务并执行压测任务。图表化压测报告直观了解压测结果。更多精彩内容,尽在华为云学院,上云体验吧!
  • [上云精品] 星云精准测试助力“2020年中国银行业软件测试成果交流会”圆满召开,聚焦金融领域测试创新
    11月13日,“2020年中国银行业软件测试成果交流会”在北京召开。此次会议由中国金融出版社主办,《中国金融》、《金融博览》、《金融博览•财富》杂志承办。来自人民银行、商业银行、第三方机构、院校及相关企业的逾两百位领导和专家出席了会议。会议围绕银行软件测试工作如何在互联网时代快速响应高质量交付的需求、如何对采用新技术的金融产品开展测试和质量评估、如何保障底层技术架构平稳升级等问题进行了深度交流,旨在凝聚行业合力、促进协同发展,为银行业软件测试转型创新起到引领和铺垫作用。中国人民银行科技司副司长罗永忠出席会议并致辞,中国金融学会金融科技专业委员会秘书长杨竑发表了主旨演讲,中国金融出版社总编辑郭建伟主持了会议,中国金融出版社副社长程建国出席会议。星云测试作为协办单位,参加2020年中国银行业测试成果交流会。 大会上,星云测试分享汇报了“星云精准测试在数据追溯中的作用“。星云测试副总经理张女士介绍说:自2012年星云测试开始启动商业产品的研发以来,精准测试技术体系经过多领域大型系统对接应用的严格考验。金融领域中,星云测试在几百个系统中成功对接实施,整体代码量超过数十亿行,对各种标准和特殊架构的支持度也非常流畅。为了给用户减少一切不必要的切换成本,星云团队还花了大量时间研究静默式精准测试方案,把用户在用的、需要对接的平台和精准测试从底层数据对接起来,像做接骨手术一样,精准无误。这样一线的测试人员基本不用添加任何额外的学习成本,以前如何操作现在依然如何操作。而管理人员,将得到所有精确的数据追溯结果。这个过程可以贯穿单元、集成、功能、自动化、验收、回归测试等整体软件生命周期,对被测系统的干扰为零。精准测试可以运行在系统级,支持几百上千人的并发使用。由于强大的数据追溯能力,使一些高级测试技术得以实现,比如在实时覆盖率可视化,回归测试选取、用例的聚类分析与智缺陷定位、在分布式系统上进行全程追溯等。精准测试的数据追溯展示测试用例的海量代码运行路径信息,并基于这些精确的数据进行测试辅助分析为人工测试提供算法支撑。在整体价格上面,星云测试基于自主技术已经完全突破白盒技术钳制、实现弯道超车,使企业大规模应用处于合理的价格区间。在解决方案的配合上,更加灵活机动,可以随时响应。星云测试有非常旺盛的持续创新力,包括Wings-企业级单元测试自动编码引擎、智能测试产品等,均在同时进步中,星云测试将继续以独立软件测试工具供应商的身份,持续的为整个行业提供最有价值的产品。
  • [部署上线] 【ModelArts产品】【在线预测功能】通过AK/SK认证方式用postman调用接口测试在线预测,报错
    【功能模块】ModelArts>部署上线>在线服务【操作步骤&问题现象】1、使用调用指南的API接口地址,通过AK/SK认证方式测试2、已通过python脚本获取签名3、将X-Sdk-Date和Authorization参数放入postman请求头,点send后报错"Incorrect IAM authentication information: verify aksk signature fail"
  • [技术干货] 10个自动化测试框架,测试工程师用起来
    软件行业正迈向自主、快速、高效的未来。为了跟上这个高速前进的生态系统的步伐,必须加快应用程序的交付时间,但不能以牺牲质量为代价。快速实现质量是必要的,因此质量保证得到了很多关注。为了满足卓越的质量和更快的上市时间的需求,自动化测试将被优先考虑。对于微型、小型和中型企业(SMEs)来说,自动化自身的测试过程是非常必要的,而最关键的方面是选择正确的自动化测试框架。什么是自动化测试框架?自动化测试框架是为自动化测试脚本提供执行环境的脚手架。框架为用户提供了各种优势,帮助他们有效地开发、执行和报告自动化测试脚本。它更像是一个专门为自动化组织的测试而创建的系统。简而言之,我们可以说框架是各种指导方针、编码标准、概念、过程、实践、项目层次、模块化、报告机制、测试数据注入等要素的建设性混合,以此支撑自动化测试。因此,用户在自动化应用程序以利用各种生产性结果时可以遵循这些指导原则。这些优势可以是不同的形式,如易于编写脚本、可伸缩性、模块化、可理解性、过程定义、可重用性、成本、维护等。因此,为了能够获得这些好处,建议开发人员使用一个或多个自动化测试框架。此外,当有一群开发人员在同一个应用程序的不同模块上工作时,以及当我们希望避免每个开发人员实现自己的自动化方法的情况下,需要一个统一的标准测试自动化框架。更多详情,请点击
  • [其他] 【Modelarts产品】【截图反馈功能】功能测试,求回复~
    【功能模块】test【操作步骤&问题现象】1、test2、test【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 基于redis-benchmark测试华为云DCS性能指导文档
    【背景】新业务上云,需要获取环境中的redis 5.0 proxy 64G集群在GET以及SET命令操作下的性能指标。 【测试目标】测试redis 5.0 proxy 64G集群服务在1000并发访问下,集群的QPS能否达到100000【环境准备】硬件环境准备服务器数量可用区用途通用计算增强型 | c6.xlarge.2   | 4核 | 8GB3华北 北京四 可用区1测试机安装测试软件Redis 5.0 proxy 64G集群1华北 北京四 可用区1被测机提供被测环境测试软件安装(基于Centos 7.5安装)注:需在每台测试机器安装redis-benchmark客户端测试工具,DCS 4.0 cluster以及DCS cluster 5.0集群需使用memtier_benchmark测试工具,使用redis-benchmark时只能测试到一个节点。其余DCS实例的均可使用redis-benchmark测试工具。1.   安装步骤$ wget http://download.redis.io/releases/redis-5.0.8.tar.gz $ tar xzf redis-5.0.8.tar.gz  $ cd redis-5.0.8/src $ make 2.    命令格式[root@testceph ~]# redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests>] [-r <keyspacelen>] [-t <tests>]参数解释:-h   <hostname>服务器的主机名(默认值为127.0.0.1)-p   <port>服务器的端口号(默认值为6379)-s   <socket>服务器的套接字(会覆盖主机名和端口号)-a   <password>登录Redis时进行身份验证的密码。-c   <clients>并发的连接数量(默认值为50)-n   <requests>发出的请求总数(默认值为100000)-d   <size>SET/GET命令所操作的值的数据大小,以字节为单位(默认值为2)–dbnum <db>选择用于性能测试的数据库的编号(默认值为0)-r   <keyspacelen>随机键个数-t  <tests>测试的命令【环境拓扑】  【测试场景】场景场景描述测试命令检查点场景1使用1个客户端,总并发数为1000,总请求数为10000000,使用1000000个随机键,操作的数据大小为32字节,操作的命令为SET和GET./redis-benchmark -h 集群地址 -p 6379 -a 密码 -c 1000 -n 10000000  -r 1000000 -d 32 -t SET,GET分别查看SET以及GET下的QPS大小场景2使用2个客户端,总并发数为1000,每个客户端并发数为500,总请求数为10000000,使用1000000个随机键,操作的数据大小为32字节,操作的命令为SET和GET./redis-benchmark –h 集群地址-p 6379 -a密码-c 500 -n 10000000  -r 1000000 -d 32 -t SET,GET分别查看SET以及GET下的QPS大小场景3使用3个客户端,总并发数为1000,每个客户端并发数为334,总请求数为10000000,使用1000000个随机键,操作的数据大小为32字节,操作的命令为SET和GET./redis-benchmark –h 集群地址-p 6379 -a密码-c 334 -n 10000000  -r 1000000 -d 32 -t SET,GET分别查看SET以及GET下的QPS大小场景4使用4个客户端,总并发数为1000,每个客户端并发数为250,总请求数为10000000,使用1000000个随机键,操作的数据大小为32字节,操作的命令为SET和GET./redis-benchmark –h 集群地址-p 6379 -a密码-c 250 -n 10000000  -r 1000000 -d 32 -t SET,GET分别查看SET以及GET下的QPS大小场景5使用6个客户端,总并发数为1000,每个客户端并发数为167,总请求数为10000000,使用1000000个随机键,操作的数据大小为32字节,操作的命令为SET和GET./redis-benchmark –h 集群地址-p 6379 -a密码-c 167 -n 10000000  -r 1000000 -d 32 -t SET,GET分别查看SET以及GET下的QPS大小场景6使用9个客户端,总并发数为1000,每个客户端并发数为111,总请求数为10000000,使用1000000个随机键,操作的数据大小为32字节,操作的命令为SET和GET./redis-benchmark –h 集群地址-p 6379 -a密码-c 111 -n 10000000  -r 1000000 -d 32 -t SET,GET分别查看SET以及GET下的QPS大小【测试结果】按照以上场景,使用3台压测机,分别在不同的客户端数量下进行并发访问,控制总并发数为1000,具体实验数据如下:由上述测试数据可得出,华为云redis 5.0 proxy 64G集群在1000并发下,可以支撑SET的QPS:27W,GET的QPS:28W,完全满足实验目标的QPS:10W。 
  • [技术干货] 《ONAP技术详解与应用实践》读书笔记25
    终于收尾了~刚看目录的时候,还以为是类似从业资格的认证呢,第10章 ONAP社区的xNF/NFVi认证计划ONAP社区有很多Usecase,比如CCVPN Usecase, SG Usecase等。我们在实现每个Uscase的时候,都是通过Service和Resource去描述Usecase中用到的业务或资源。这些Resource可以是VNF,也可以是PNF ( Physical Network Function,物理网络功能)。简单来讲, Service就是通过不同的VNF/PNF加上之间的Connectivity组成。那如何保证这些VNF/PNF在进入ONAP系统之前已经被充分测试过,并且兼容ONAP系统以及运营商对于VNF/PNF的诉求,从而简化VNF/PNF的采购流程呢?我们需要一个持续集成、持续交付的工具链来测试认证VNF/PNF,它需要满足如下要求:满足不同领域对于VNF/PNF的诉求,如ETSI,3GPP, ONAP或者运营商自己的标准;运营商可以结合自身业务的诉求,通过工具来加速测试入网的流程;通过建立一个运营商、设备商都认可的组织来制定VNF/PNF测试的标准、用例从工具以及生态两方面来加速VNF/PNF的交付与采购。基于以上诉求, LF ( Linux Foundation)结合OPNFV社区的NFVi/VIM认证和ONAP社区的VNF认证,在LF Networking (Linux Foundation Networking)下成立了CVC( Compliance and Verification Committee, 兼容认证委员会),来覆盖NFVi/VIM认证和VNF/PNF认证。后续章节,我们将使用xNF来指代VNF和PNF。测试认证中的On-Board/Onboarding/Pre-onboarding概念:在ETSI中, On-Board概念指的是上传加载NSD/VNFD等包到NFVO系统的过程。在ONAP中, Onboarding的概念包含了对VNFD/PNFD包本身的测试验证以及上传到ONAP系统去设计对应的Service,通常意义上,我们认为onboarding是发生在ONAP的SDC项目内, Pre-onboarding是发生在ONAP VNFSDK项目内,因为只有通过VNFSDK的测试,我们才会认为这个VNF Package对于ONAP系统来说是可用的。10.1 CVC认证及其对于产业的价值CVC是 LF Networking中成员驱动的委员会,负责向LF Networking的董事会提供认证测试规范以及对应的兼容测试工具 CVP ( Compliance Verification Program), 用于测试包括NFVi/VIM、XNF在内的多种商用产品。 CVP是一套测试流程和测试工具,承接CVC对NFVi和xNF等产品的认证工作。CVP第一个版本的测试框架基于OVP ( OPNFV Verification Program)扩展而来,它以OPNFV社区的Dovetail项目为整体框架,来集成其他的测试工具。CVP已经集成部分来自OPNFV社区和ONAP社区的工具。其中, OPNFV社区工具包括Functest, Bottlenecks和Yardstick,分别对应NFVi/VIM的功能测试、压力测试和性能可靠性测试; ONAP社区工具是VNFSDK,提供基于TOSCA模板的xNF测试。CVP旨在提供一个持续集成、持续交付(CI/CD)工具集,来运行自动化测试用例,最终输出可信的测试报告,并可以管理xNF/NFVi的包以及对应的测试结果。目前ONAP中xNF的测试用例主要来源于两个项目:一个是VNFSDK项目,一个是VVP项目,其中VNFSDK主要是用来做TOSCA语言的VNFD校验,VVP主要用来做HOT语言的VNFD校验。如上所述,如果把ONAP比喻成一台打印机的话,通过了cvC认证的xNE就是通过了认证的耗材,可以方便的即插即用,实现快速上线。运营商选择通过CVC认证的xNF产品,能够大大减少后续的调测、上线和运维成本。对于供应商来说,通过CVC认证可以基于ONAP快速准入上线,节省了标书澄清、测试境搭建以及验证报告输出的成本,同时可大幅缩短产品的上市时间。可以预见,在供给侧对CVC认证逐渐重视的情况下, CVC认证将变成一种共识和事实标准。当CVC认证变成整个xNF设计、开发、发布、认证、采购、上线各个环节的标准后,采购环节的流程的流程将会大大缩减,采购速度将会大大提高,同时能够有效降低运营商断和设备采购商的成本。10.2 CVP的架构组件及其主要功能CVP的架构设计起源于OPNFV Verified Program项目,并在OVP基础上扩展出了xNF测试,后续会继续增加基于LFN下所有项目的测试。CVP整体架构及组件:其中,最上层是Dovetail,作为CVC认证测试的入口,由它来集成其他的测试工具;中间层是测试工具集合,实现对应NFVi/VIM和VNF/PNF的各种认证测试;下层是NFVi/VIM, xNF等被测系统(SUT,System Under Test)。在CVC成立之前, Dovetail工具是OPNFV社区OVP认证的测试工具。CVC成立后CVP复用Dovetail作为CVC认证测试的人口,由Dovetail集成其他开源测试工具,来实现对NFVi/VIM和xNF等商用产品的认证测试。VNFSDK包含VNF包验证( Package Validation)工具、生命周期测试框架( LifecyeleTest)、功能测试框架( Functional Test)和xNF仓库( Reference Repository/Markeplace)。VNFSDK希望通过开发一套CI/CD的验证测试工具,来对供应商的xNF进行测试,以构建一个兼容ONAP的xNF的生态系统。ONAP社区还有一个测试工具VVP ( VNF ValidationProgram),用于测试基于Heat模板的VNF。Functest是OPNFV社区的一个测试项目,最初定位为针对VIM和NFVi组件提供功能测试方法、测试用例和测试套件,以验证OPNFV平台VIM和NFVi组件的功能,确保平台已被正确的安装,且各个组件能够正常工作。Functest项目的部分用例来源于上游社区,如Openstack, Kubernetes等。这些用例主要涵盖健康度检查、API接口测试、VNF功能测等。Yardstick是OPNFV社区的性能测试项目,从VNF的角度来对NFVi进行性能测试。Yardstick涵盖计算、存储和网络相关的NFVi性能测试和针对OpenStack的可靠性测试。Yardstick通过将典型VNF工作负载分解成对应性能指标并进行测试,来达到评估NFVi性能和可靠性的目的。Bottlenecks也是OPNFV社区的一个性能测试项目,主要是对NFV所依赖的NFVi进行高负载的压力测试。Bottlenecks 通过构建一套压力测试场景,收集测试过程中的各种NFVi性能指标,以找到NFVi的瓶颈。 Bottlenecks的压力测试场景包括高吞吐量测试,并行VM生成、销毁, VM扩展,以及长时间的浸泡测试等。除了上述测试工具外, CVP还包含OCLI (ONAP Command Line Interface)组件和Dash-board。 OCLI提供所有VNFSDK项目中命令行的执行能力,以供Dovetail通过命令行调用测试脚本。Dashboard接收Dovetail输出的测试结果,通过CVC委员会的评审后,展示最终的测试结果以及被认证的产品信息。10.3如何使用CVP认证你的xNF过部署CVP工具,用户可以在本地或委托第三方实验室来认证自己的xNF产品。目前社区已经在开发CVP的Portal,以便用户可以一键式安装CVP相关的工具以及通过页面触发相关的xNF/NFVi测试。10.3.2通过第三方实验室认证xNF有两种方式可以执行xNF Test的测试用例:一种是在Dovetail上直接执行测试用例并获取返回结果;另外一种方式是在VNFSDK命令行中执行测试用例,也可以获得结果,但是在VNFSDK命令行中执行脚本获取的测试结果格式不能直接在Dashboard中评审。10.4如何定制你的xNF认证内容10.4.1 自定义CVP的测试脚本范围本范围CVC的测试范围包含NFVi和xNF两部分。本章我们主要描述如何定义xNF的测试脚本的范围。xNF具体的测试用例来源包含以下几部分:ONAP VNF Requirements项目ONAP Modeling子委员会运营商自定义需求标准需求(如ETSI SOL001,SOL004等)VNFSDK提供API,给用户自定义测试用例的范围。通过定义不同的场景,来选择不同测试用例,可以包括第三方的用例。具体支持的操作如下:列出VNFSDK目前支持的所有场景: /vtp/scenarios。列出给定场景所有的测试套件: /vtp/scenarios/{scenario}/testcases。列出所有在给定场景下可用的测试用例: /vtp/scenarios/{scenario}/testcases。执行测试用例: /vtp/executions。其中执行测试用例,需要在请求body体中,包含用户所有的自动测试用例。10.4.2参与社区的标准用例制定CVC (VNFSDK)会在每个版本的开始阶段,从上述不同的测试用例源中,选取这个版本可实现的测试用例集合来实现。针对不同的测试用例来源,在社区不同的项目或者委员会里面讨论。ONAP VNF Requirements :如果认为原有VNF Requirements需求描述不完全或者不合理,希望更新用例,或者有Usecase或者其他框架性的功能需求,需要新增需求,可以在JRA (Jira.onap.org)上创建对应JIRA单,然后到VNF Requirements项目中去讨论,获取同意,以便在后续版本中实现。标准更新(Modeling子委员会):目前社区相关讨论比较多的是ETSI NEV相关标准。其中ETSI SOL001制定VNF Descriptor,NS Descriptor, PNF Descriptior相关规范。ETSI SOL004定义对应的是Package规范。ONAP一般是同步最新的标准期范(如ETSI SOL)到社区,并进行适当的修改,来适应ONAP对应的需求。标准的定义讨论是在Modeling子委员会中,也可以在Wiki提交自己的提案并在社区会议中讨论。
  • [交流吐槽] 张小白的自动化测试工具折腾记(1)TestNG(连载中)
    敏捷开发和Devops给测试提出了相当高的要求,自动化测试工具也应运而生。从目前笔者检索到的资料来看,自动化测试工具有以下几种:(1)JUnit——Java语言的单元测试框架,另外还有衍生的NUnit(.NET的单元测试框架)等xUnit家族,如CPPUnit等等。(2)Postman——强大的Web API & HTTP 请求调试工具。(3)Selenium——Web应用的自动化测试工具,用来模拟用户在浏览器的操作。(4)Appnium——移动端(支持Android和IOS)的自动化测试工具。(5)Robot Framework——功能自动化测试框架。这是某些大厂常用的测试框架。(6)HttpClient——支持HTTP协议的客户端程序包。(7)TestNG——接口自动化测试工具,TestNG在JUNIT和NUnit的基础上增加了很多功能,支持单元自动化测试,还可以集成Selenium,Appnium和HttpClient等框架进行各种类型的自动化测试。(8)RestAssured——基于REST服务的测试过程的Java DSL。可以方便的处理REST请求并处理响应信息。(9)JMeter——基于Java的压力测试工具。(10)LoadRunner——老牌的压力测试工具。。是个收费的商业软件。我们看到,华为云推出了很多API服务,我们就以API测试最常见的TestNG,通过实例对API测试进行讲解,以此来理解一下TestNG的用法。以笔者常用的eclipse为例。首先我们需解决安装TestNG的问题。由于众所周知的原因,我们需要先配置maven的阿里云镜像(当然是否也可以用华为云镜像,读者也可以试一下)修改Users/zhang/.m2/settings.xml文件:我们建立一个Maven工程,并在工程内修改POM.xml文件如下:这样,Maven会自动下载TestNG依赖包。我们再通过eclipse的菜单Install New Software安装TestNG插件:在work with栏输入地址:http://dl.bintray.com/testng-team/testng-eclipse-release/ 并按回车键,系统会检索出TestNG:选中然后安装即可。我们在src/main/java的相关package下新建个HelloTest.java的文件,内容如下:package com.zhanghui.testing; import org.testng.annotations.Test; public class HelloTest { @Test public void testCase1() { System.out.println("Hello Zhanghui!"); } }在代码上点击右键-》RunAs=》TestNG Test系统控制台会出现以下内容:表明TestNG安装和测试成功。Suite表示测试套件,是对多个测试类进行组合而成的suite.比如用于测试基本流程的套件,用于测试促销功能的套件,甚至用于进行冒烟测试的套件等等。其中,TestNG还支持如下注解:@BeforeSuite @AfterSuite注解——在该套件(Suite)的所有测试之前/后运行,仅运行一次@BeforeClass和 @AfterClass注解——在调用当前类的第一个测试方法之前/后运行,仅运行一次@BeforeTest和@AfterTest注解——在属于test标签内的类的所有测试方法运行之前/后运行@BeforeMethod和@AfterMethod注解——在每个测试方法之前/后都运行@BeforeGroups和@AfterGroup注解——在每个group之前/后运行我们用实例示范一下:package com.zhanghui.testing; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterGroups; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterSuite; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeGroups; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; public class BeforeAfterTest {   @Test(groups="group1")   public void test1() {   System.out.println("test1 from group1");   Assert.assertTrue(true);   }      @Test(groups="group1")   public void test11() {   System.out.println("test11 from group1");   Assert.assertTrue(true);   }      @Test(groups="group2")   public void test2()    {   System.out.println("test2 from group2");   Assert.assertTrue(true);   }   @Test(groups="group2")   public void test21()    {   System.out.println("test21 from group2");   Assert.assertTrue(true);   }   @BeforeTest   public void beforeTest()    {   System.out.println("beforeTest");   }      @AfterTest   public void afterTest()    {   System.out.println("afterTest");   }      @BeforeClass   public void beforeClass()    {   System.out.println("beforeClass");   }      @AfterClass   public void afterClass()    {   System.out.println("afterClass");   }      @BeforeSuite   public void beforeSuite()    {   System.out.println("beforeSuite");   }      @AfterSuite   public void afterSuite()    {   System.out.println("afterSuite");   }      //只对group1有效   @BeforeGroups(groups="group1")   public void beforeGroups()    {   System.out.println("beforeGroups1");   }      //只对group1有效   @AfterGroups(groups="group1")   public void afterGroups()    {   System.out.println("afterGroups1");   }      @BeforeMethod   public void beforeMethod()    {   System.out.println("beforeMethod");   }      @AfterMethod   public void afterMethod()    {   System.out.println("afterMethod");   } }结果如下:[RemoteTestNG] detected TestNG version 7.3.0 beforeSuite beforeTest beforeClass beforeMethod test1 from group1 afterMethod beforeMethod test11 from group1 afterMethod beforeMethod test2 from group2 afterMethod beforeMethod test21 from group2 afterMethod afterClass afterTest PASSED: test1 PASSED: test11 PASSED: test2 PASSED: test21 ===============================================     Default test     Tests run: 4, Failures: 0, Skips: 0 =============================================== afterSuite =============================================== Default suite Total tests run: 4, Passes: 4, Failures: 0, Skips: 0 ===============================================我们来做个简单的分析:Suite>Test>Class>Method. TestNG会根据本class中的以上顺序依次完成相关的Before注解的执行,然后在测试结束时依次倒过来完成After注解的执行。它也会根据每个 @Test注解的group,依次将本group下的所有test执行完,再执行下一个group.
  • [技术干货] 【转载】软件测试丨自动化测试之读取配置文件
    原文链接:https://bbs.huaweicloud.com/blogs/196994在日常自动化测试开发工作中,经常要使用配置文件,进行环境配置,或进行数据驱动等。我们常常把这些文件放置在 resources 目录下,然后通过 getResource、ClassLoader.getResource 和 getResourceAsStream() 等方法去读取。经常看到有不少同学在读取配置文件时踩坑,本人也是一路踩坑摸索过来,这里做一个简要梳理,供大家参考。一、何为 classpath ?读取资源文件最关键的就是找到文件的位置,归根结底就是找路径,而怎么找,在哪找就是个问题。这其中和 classpath 有很大关系,因此我们先了解下 classpath 的概念,帮助理清思路。我们用 Java 编写的文件都是 .java 文件,而想要运行,还需将其编译成 .class 字节码文件才可被 JVM 运行;这就需要 JVM 先找到对应的 .class 才行,这也就是要找到对应的classpath。JVM 会在编译项目时,会主动将 .java 文件编译成 .class 文件 并和 resources目录下的静态文件一起放在 target/classes (如果是 test 下的类,便会放于 /target/test-classes 下)目录下;现有工程目录如下:编译后进入 target 目录下查看如下:二、class.getResource()先来看 getResource 的用法先分别执行如下测试代码,打印带有"/"和不带"/"的path    import org.junit.jupiter.api.Test;    public class ResourceTestDemo {        @Test        void getResourceTest(){            System.out.println(ResourceTestDemo.class.getResource(""));            System.out.println(ResourceTestDemo.class.getResource("/"));           }打印结果:    file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/test-classes/resourcetest/    file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/test-classes/结果分析:1、getResource("")不带"/“时候是从当前类所在包路径去获取资源;2、getResource("/")带”/"时候是从classpath的根路径获取;现在来尝试获取resources下的文件2.txt和3.txt:测试代码:    @Test     void getResourceFileTest(){         System.out.println(ResourceTestDemo.class.getResource("/3.txt"));         System.out.println(ResourceTestDemo.class.getResource("/test/2.txt"));     }打印结果:    file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/3.txt    file:/Users/username/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/test/2.txt三、getClassLoader().getResource()和上述一样,先分别执行测试代码,打印带有"/"和不带"/"的path:     @Test     void getClassLoaderResourceTest(){         System.out.println(ResourceTestDemo.class.getClassLoader().getResource(""));         System.out.println(ResourceTestDemo.class.getClassLoader().getResource("/"));     }打印结果:    file:/Users/qinzhen/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/test-classes/    null结果分析:1、 getClassLoader().getResource("")不带"/“时候是从classpath的根路径获取;2、 getClassLoader().getResource("/")带有”/"打印为null,路径中无法带有"/"现在继续尝试获取resources下的文件2.txt和3.txt:@Testvoid getClassLoaderResourceFileTest(){    System.out.println(ResourceTestDemo.class.getClassLoader().getResource("3.txt"));    System.out.println(ResourceTestDemo.class.getClassLoader().getResource("test/2.txt"));}打印结果:    file:/Users/qinzhen/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/3.txt    file:/Users/qinzhen/Documents/TestDev/MyTraining/XUnit/ResourceTest/target/classes/test/2.txt四、getResourceAsStream()getResourceAsStream() 方法仅仅是获取对应路径文件的输入流,在路径的用法上与getResource()一致。补充其实当我们查看 class.getResource 的源码时发现如下:public java.net.URL getResource(String name) {    name = resolveName(name);    ClassLoader cl = getClassLoader0();    if (cl==null) {        // A system class.        return ClassLoader.getSystemResource(name);    }    return cl.getResource(name);}  其实这里也是调用了getClassLoader,只是方便了我们使用而已。总结class.getResource()不带"/"时候是从当前类所在包路径去获取资源;class.getResource()带"/"时候是从classpath的根路径获取;class.getResource()本质上也是调用了getClassLoader,只是封装了一层方便了我们使用而已;getClassLoader().getResource("")不带"/"时候是从classpath的根路径获取;getClassLoader().getResource("/")路径中无法带有"/";getResourceAsStream() 方法仅仅是获取对应路径文件的输入流,在路径的用法上与getResource()一致;以上,供大家探讨。更多技术文章分享及测试资料
  • [技术干货] 【转载】软件开发丨关于软件重构的灵魂四问
    程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。摘要在本文中,您会了解到如下的内容:先添加新功能还是先进行重构?重构到底有什么价值?如何评判这些价值?重构的时机是什么?如何进行重构?1. 先添加新功能还是先进行重构?                                 问题:官方资料,重构分析1.0版中。有两顶帽子,一个是添加新功能,一个是重构添加新功能时,你不应该修改既有代码,只管添加新功能,重构时你就不能再添加功能,只管改进程序结构。一次只做一件事情。这两个是否有矛盾,以哪个为准?前面有些可信材料版本不一,有的还要互相打架,是否可以统一一下?回复:关于添加新功能和重构是否矛盾的问题,是先添加新功能还是先进行重构?我们要做的是观察这两个事情哪个更容易一些,我们要做更容易的那一个。就是你不能一下子同时做这两件事情。因为同时做两件事情,会导致你工作的复杂度提升,容易出错。一般而言,重构会改变程序的设计结构改动相对来说比较大。但是因为没有功能方面的添加,所以对应的测试案例我们不需要进行修改,那对我们来说,只要能够使得现有的重构修改能够满足我们的业务测试案例就可以了。添加新功能意味着我们要添加对应的测试案例,以保证我们新的功能是可测的。这部分的修改一般会依托现有的程序结构,改动起来相对比较少,并且修改容易鉴别。在绝大多数正常情况下,我们一般是先添加功能,提交完成以后,再新的修改需求中对代码进行重构。从大的方向上来说是分两步走的,这两个任务不能混为一谈。一次只做一件事情,一次提交只包含一个任务,这是为了避免在工作中人为的增加复杂度,这个复杂度包含代码修改,审查,测试等各个方面。避免复杂度的上升,是我们在软件开发过程中时刻要谨记的一个原则。俗话说,一口吃不成胖子,心急吃不了热豆腐。做事情要一步一个脚印,稳扎稳打,步步为营。2. 重构的价值和评判效果问题:哪种类型的代码重构是高价值的?1. 在网上跑了这么多年也没啥问题,为什么要动他?2. 重构前后功能又没啥变化,当前收益是啥?3. 若是提高可维护性,可扩展性的话,怎么评判效果呢?回复:这是关于重构价值和评判结果的问题。这几个问题问的都很好。我们来看第1个问题,就是"在网上跑了这么多年也没啥问题,为什么要动"的问题?这里的关键点就在于到底有没有问题。是不是说在客户那边客户看不到问题,就算是没问题。当然不是的,在我们软件开发当中,在交付给客户以后,客户那边看到的是黑盒,他不知道我们内部的逻辑存在多少的漏洞。如果我们的内部逻辑存在很多的漏洞。假设偶然某一天,某个客户发现了一个漏洞,它可以通过这一个漏洞进入到我们的系统内部,这样进入我们的内部,会发生什么样的状况,我们可以自己想象。在公司的内部发言中专门提到了UK对我们产品的一个评价,外层是铜墙铁壁,内层是很脆弱的,客户或者黑客一旦进入到我们的内部以后,他就可以为所欲为了,从这一点上来说,我们一定要对我们现有的代码进行重构,以避免这样的问题。我们再来看第2个问题。重构前后功能又没啥变化,当前收益是什么?重构最大的收益是解决如下的问题:代码太多重复问题,单个函数体或者文件或者攻城过大的问题,模块之间耦合度太高的问题等等。以上问题归根结底就是一个问题,就是复杂度过高的问题。现在来谈一谈复杂度的问题,软件开发中的复杂度当然是越低越好。一般谈到复杂度,我们可能想到了各种逻辑上的复杂度,设计上的复杂度,实际上在软件过程中复杂度涉及到方方面面,我们来看一下,具体有哪些方面我们需要注意复杂度的问题。第一是命名规则。先举个例子,我定一个变量叫word。有的人喜欢把它写成wd。这个就增加了这个变量定义的复杂度,你从wd很难明白,这个变量是word的意思。不管是变量的命名还是函数的命名,我们都希望看到名字,我们应该能够理解这个变量或者函数大体是关联到什么样子的事情。所以谨慎的使用缩写是避免命名规则复杂度提高的重要前提。第二是程序逻辑的复杂度。线性顺序执行的复杂度为1, 出现分支以后要乘以分支的个数。分支可以是条件判断也可以是循环。所以尽可能的避免分支的出现是降低程序逻辑复杂度的重要手段。如果程序分支不可避免,要尽可能的把程序分支放到最高的逻辑层。这样做的目的是为了避免在下层处理的时候出现发散式的分支。发散式的分支会急剧的增加程序的复杂度。复杂度越高,程序越难维护,复杂度超过一定程度,人类程序员是无法处理的。第三是架构设计的复杂度。架构设计涉及到模块设计和系统设计。要尽可能的把一些公用的模块或者子系统抽取出来,比如安全相关的,日志相关的,工具相关的等等,这些公用的功能可能会被所有其他的业务模块或系统所调用。在调用这些公用功能的时候,越简单越好,并且调用者不需要关心具体的内部实现,只需要知道如何使用就可以了。这样做的目的是让程序员专注到业务代码的设计上来。第四是系统部署的复杂度。系统部署包含几个不同的阶段如开发阶段,测试阶段和生产阶段。不管是哪个阶段,部署的步骤越少越不容易出错。有些系统天然的需要很多指令的配置,如果是这样的情况,需要编写一个批处理的文件来简化外部使用者的部署步骤,把多个步骤变成一步。与部署相关联的还有集成部分。如果能够实现自动化或者从模板中创建那是非常好的状态。第五是测试的复杂度。测试分白盒测试和黑盒测试。白盒测试的复杂度直接关联着代码层级的复杂度,代码层级的复杂度越高,当然白盒测试的复杂度也就越高。白盒测试需要注意的一个重要问题是不要使白盒测试这部分的代码脱离实际业务代码的设计。也就是说白盒测试它的依附对象就是我们实际的业务代码,从架构设计上说是一个附属层,不要试图在这里使用什么软件设计艺术或者所谓的编程艺术。这种代码的风格就是简单直接,复杂度线性化。黑盒测试的复杂度来自于业务需求分析。要有非常清晰的文档说明,需要对测试步骤和预期结果写的非常清楚。第六是技术的复杂度。技术的发展趋势一般是越发展越简单,功能越强大。那么在设计和开发的过程中,要避免使用老旧的技术。关于技术框架的选择,要提前做好调研。前端选什么框架,要不要选择某些UI库,后端选什么框架,要不要选择某些程序库,原则上是为了简化我们的学习过程,提高开发效率,增强整个项目的可维护性。需要具体问题具体分析。第七是队伍结构的复杂度。队伍构成一定要短小精悍,人多不一定好办事。像亚马逊提倡的是两张披萨团队,意思是说整个团队两张pizza就能吃饱。大体估算就是10人左右的一个队伍。当然这只是一个参考指标。整个队伍的目标一定要明确。所有的人都向着那个目标迈进,分工可以不同,但是目标一定要一致。目标+分工是队伍成功运作的关键。具体来说就是把目标分成多个任务,每个任务里又可以分成小任务,那所有的人都去做对应的任务,自己让自己忙起来,而不是别人让你忙起来。我们现在来看一下第3个问题,就是如何评判重构效果的问题。在上面的分析中,我们已经了解了重构的目标和最大的收益,就是复杂度的降低。那么对应的,就是代码的重复率大大降低了,单个函数体或者代码文件或者工程过大的问题不存在或者减少了,模块之间的耦合性降低了。再进一步说,就是关于代码的可维护性和可扩展性上,我们需要关注这么几点:一是代码的可读性,我们看到现有的代码就应该可以理解代码作者的意图是什么,这样我们在修改bug的时候就更容易把握。比如函数,类或者组件的功能要单一化,命名要友好,要删除一些误导性的注释,对于一些没用的代码,要毫不客气的抛弃。二是设计模式的可参考性。设计模式的好处就是提供一种可以追寻的代码扩展轨迹,新的功能可以遵循这种轨迹模板进行添加,从现在就说一下白盒测试这一部分。测试的框架应该在项目开始阶段或者重构开始前搭起来。等部分代码成型的时候,逐步的添加必要的测试案例。测试案例的选取可以按照环形复杂度的计算方法来确定,也可以根据集成测试对应的用户需求来确定。与代码相关的测试,一般有单元测试,集成测试和系统级的测试。单元测试,一般被认为非常繁琐。单元测试的繁琐主要体现在测试案例的选取上, 如果使用全覆盖方式来选取测试案例的话,会产生大量的测试代码,以后维护起来也是一个负担。如果采用环形复杂度来选取测试案例的话,会产生适量的测试代码,但是环形复杂度的计算也是一个很大的时间开销。集成测试跟客户的实际业务需求相关。在这个过程中需要理清接口的输入与输出,以及运行路径,然后据此来设计测试案例,写出测试案例代码。开发人员一般不会拒绝写集成测试。因为她带来的好处是实实在在的,会极大的提高你的开发效率和调试效率。尤其是对于无界面的程序接口尤为重要。系统级测试是大系统中子系统之间的集成测试。这个主要包含两个方面:一个方面是有界面的自动化测试,通过这样的测试架构来模拟人类用户的使用过程,同时增加一些随机性的行为,试图能够找出系统的一些漏洞。另一种是无界面的测试,体现在多个服务系统之间的调用上或者类似浏览器自动化框架的使用上。一套完整的测试系统,可以帮助工程师提高开发效率,减少以后系统维护和重构的成本。从测试的紧迫性上来说,集成测试最为必要,系统间的测试有时候使用手工测试通过一些测试工具来代替。单元测试可以有很广阔的讨论空间,这部分要具体问题具体分析。3. 重构的时机问题:关于重构时机的说法,正确的是?添加功能时,重构能够使得未来新增特性时更快捷、更流畅在修复错误时,应该聚焦问题本身,不建议重构,可以避免引入新的问题专家Review时重构,能够传递经验,改善设计,避免或减少代码持续腐化回复:关于重构的时机问题,现在我们有三个选项,我们就分别分析一下这三个选项。第1个选项是说在添加功能的时候进行重构。这个选项的主要问题就是一个提交包含了多个任务。这属于人为的增加工作的复杂度。第1个缺点是会增加工作的难度,使得本来可以用工作量1解决的问题,变成了工作量2和3。第2个缺点是增加了代码审查的难度。本来你的提交中描述的是添加功能,结果发现里面的代码修改大部分与此描述无关。所以第1个选项排除。第2个选项是说在修复错误的时候应该聚焦问题本身,不建议重构,以避免引入新的问题。聚焦是点睛之笔。我们在做任何事情的时候,都不要忘记初心,集中精力攻克问题,不要分心。所以第2个选项是正确的。第3个选项是说专家在审查代码的时候再重构。这里面的最关键问题是专家可能并不了解代码的业务需求和应用场景。他们能够看到代码存在不好的味道,但在不了解业务场景的情况下,让专家进行重构会带来很大的风险。所以第3个选项也不正确。4. 如何进行重构?问题:如何正确的进行重构?回复:下面我们来看看如何进行重构。简单的代码重构我们都比较熟悉,比如说你通过工具就可以做一些整理,如变量重命名,函数抽取,类创建等等。现在比较头疼的一个话题就是对老产品的重构,一些老产品涉及到上千万行,上亿行的代码。关于老产品整改的问题。如果只是缝缝补补的话,可能起不到化繁为简的目的。其实做类似这种工作的话,有一个比较可行的方案。就是把现有的产品当做一个成型系统也就是现有运行的产品,不要做大的改动,顶多就是修改bug。然后以这些成型的系统为基准,去写新的系统。相当于参照一个大的白盒就写一个小的白盒,这样新的小的白盒质量上肯定比大的白盒性能上要有优势。这样子按部就班去做的话,就会比较靠谱。有朋友会说上面的做法是重写,字面意义上没错的。实际上不矛盾。区别就是重构的方式应该从下往上还是从上往下。比如说我们现在大部分的重构都理解为从下往上来做。也就是感觉这个文件里头有坏代码的味道,然后就改这个文件,这样做是没有问题的。比如现在有些教练遇到的问题,就是发现上下文不是很清晰,这个代码为什么要这么写?为什么一个文件有1万行或者3万行,这个来龙去脉不是很清楚。这个时候可能就需要从整个子模块来进行一个自上而下的分析。梳理出这个子模块的功能需求是怎样的,需要有多少个公共接口?内部公共接口的实现方式是不是应该像目前这样的?一个文件能够写成1万行或者3万行,肯定是有一定历史原因的,绝大程度是由于全局把握的编程能力不够造成的。像这种情况,如果从这个文件本身去做重构的话,难度非常之大,但是如果从上往下,从模块的整个设计角度来做重构的话,可能就容易一些。对于这样的庞然大物,最好的办法就是分而治之。首先要确定系统的功能逻辑点,针对这些逻辑点,要编排好对应的检测点,也就是说等我们完成了重构以后,我们得确保我们的重构是没有问题的,这些检测点就是做这个的,我们可以理解成集成类的测试。这些集成类的测试一定要确保可以在当前未重构之前的系统上正常运行。有了这个设施以后,我们就可以开展我们的重构工作。重构的方法有很多,比如采用比较好的工具,函数和变量的命名改变,调用方式的改变等等。这些是在现有代码的基础上进行的重构。这里我们重点说一下重写的方式来实现重构。所谓重写呢,就是另外开辟一套代码底座。甚至可以选用不同的编程语言。这种情况下重构首先要重用已有的业务逻辑,实现针对业务逻辑集成测试100%的通过率。具体不管采用哪种方式都要一个模块一个模块的进行推进。验证完成一个是一个,千万不能急于求成,试图一次性的把某些问题搞定。如果出现很多次失败,有可能会消磨掉你的自信心。所以一定要一点一点的往前推进,始终是在进步当中。采用了这种方式以后,不管当前的系统有多么的庞大,你只要坚持做下去,就一定能够把重构工作彻底完成。这个时候需要做的具体步骤可以参考如下:1. 根据功能需求定义公共接口。2. 根据公共接口写出测试案例代码。3. 这个时候可以按照测试驱动开发的理念去填充代码。4. 代码可以从现有的代码中抽取出来。5. 在抽取的过程中进行整理重构。这样,这个子模块完成以后,就可以尝试去替代现有的子模块,看看能不能在整个系统中安全的运行。  对于整个系统来说,我们又可以分成很多个子模块。然后又可以对各个子模块各个击破,最终完成对整个系统的重构。如果一开始对整个系统进行重构的话,也是可以从自上而下的角度来看的。比如说开始的时候先把所有的子模块看成一些占位符,假定他们已经完成他们的接口了。那对于整个系统来说,它本身就是一个子模块,属于提纲挈领的那个模块。这个过程,从字面意义上可以理解成重写,实际上,它也是一个重构的过程,因为我们肯定会重用这个系统本身的一些现有代码和现有的逻辑。上面我们是假定系统在已经完成的情况下进行的重构,其实重构可以贯穿于软件开发的始终。软件开发的首要目标是实现业务逻辑,能够解决客户的问题。这个目标实现以后,我们就要追求代码的干净度,复杂度能够降到最小,当前的技术能够用到最先进。所以只要有机会,我们都应该对代码和设计进行重构。结语本文针对收到的几个关于重构方面的问题作了回答,侧重点各不一样,希望能够给存在相同困惑的朋友们有所启示。 
  • [分享交流] Wings企业级单元测试自动编码引擎白皮书
    第一章Wings企业级单元测试自动编码引擎诞生的背景 (附Wings讲解视频和白皮书pdf下载)随着科技的飞速发展,软件系统越来越复杂,在系统测试阶段不断遇到的瓶颈,迫使行业逐步追根溯源到了单元测试阶段。软件缺陷发现得越晚,其处理费用就越呈几何激增,因此测试左移概念已经成为趋势。单元测试面临的最大问题是单元测试用例编写工作量巨大,极端情况下与开发工作量比达到1:1,甚至更高,这使大部分开发团队要么主动忽视单元测试,要么象征性的走个流程。如果可以让计算机先对被测试程序进行全局分析和深度理解,再由计算机进行全自动的完成单元测试编码,同时还能确保自动编写的代码无语法、语义错误的直接运行起来,这种用计算机智能算法全自动产生的测试编码去验证开发人员编写的源代码逻辑输入输出对错的高端测试模式,无疑是未来软件测试领域最为璀璨的“明珠”技术。国外软件诸如c++ test完成了这个领域的初步技术探索,星云测试研发的Wings(目前商用产品支持c/c++程序)产品,则大踏步完成了整体技术跨越和多方商用落地验证。Wings可以对程序参数进行深度解析,比如c++类、模板类、数组、结构体、指针、链表以及任意复杂结构的层级嵌套,同时对于面向对象的程序特性以及常用的容器库,能够完美识别和支持。对于一些void*、函数指针、模板类等无法直接静态分析进行类型确定的特殊情况,均有基于人工智能的程序分析辅助进行类型确定。Wings在基于深度参数解析的基础上,对于全局范围的程序进行理解分析后,第一步 按照内置规则,自动化构建被测程序的输入用例代码;第二步 构建测试代码用于调用被测程序的源代码;第三步 构建被测程序输出断言,完成调用被测试程序单元的全部环境准备。这个构建速度非常快,可以达到每分钟100万行左右的生成速度,编写的代码比程序开发人员手工编写的规范度高出一截,并确保100% 的语法语义正确,免去大量的调试时间。在驱动数据上,Wings实现了驱动代码和数据的分离。Wings基于深度参数解析基础上,可以根据参数的结构自动生成层级嵌套的测试数据结构,用图形界面可视化的展示给用户。用户只需要根据Wings提供的界面向导对测试数据进行填充即可,驱动程序会自动识别并读取这些数据,完成对被测试程序的调用。Wings还可以全自动生成参数捕获程序,并自动插装在被测试程序中。当被测试程序运行后,可以通过专用软件捕获程序中每个函数模块运行的具体参数值。Wings的测试代码驱动自动生成和参数捕获,相当于完成了一种全智能的闭环测试验证体系。Wings使测试数据不需要人工准备,只需要在前序轮次中通过参数捕获自动存储。若前序测试用例运行正常,那么这些数据都可以作为后续测试输入和进行校验的基础数据。第二章 单元测试自动生成技术2.1 测试左移后单元测试面临的问题测试左移后,传统的单元测试一般面临很多问题,主要如下:(1) 传统程序级测试用例的编写会耗费开发人员大量的工时,比如TDD测试驱动开发里面的单元测试无法有效实施,导致所有测试几乎全部依赖于系统级黑盒测试。程序级测试用例的开发成本是功能实现代码本身时间至少为1:1,绝大部分企业选择放弃开发程序级测试,而采用系统级测试方法。(2)     需求发生变化导致程序实现发生变化后,程序集用例也需要发生变化,和自动化面临的问题一样,单元测试本身的可维护性问题导致投入是持续的而不是一次性的,会打消其企业应用的热情。(3)     很多单元在未组装成系统的时候切入,如果需要进行测试需要进行大量的mock操作或者桩模拟,这个过程会造成单元测试的不精确性。(4)     程序集测试数据量很大,全部需要用户来进行准备无法全自动从前序系统测试过程中获取。      针对以上问题,很多业内人士提出了很多办法,例如自动生成测试用例、自动构建测试驱动、模糊测试方法等诸多方式,但是实际开发中程序输入参数十分复杂,简单的输入已经不能满足,如何能够构建复杂的参数输入,是要解决的重要问题。因此,星云测试研发了Wings产品--单元级的自动编码引擎,它不仅能够自动构建测试驱动,还能处理十分复杂的大型程序参数,帮助企业能够更好的进行单元测试。2.2 完成单元测试要素构成单元测试的要素如下:a.        测试数据b.        测试驱动代码例如针对以下程序,需要准备的测试输入以及测试代码① 全局变量:c② 参数:a、b③ 预期输出:320 30 int c = 100; int sum(int a, int b) {      return a + b + c; } int driver_sum() {      int a = 100, b = 20;      c = 200;      return sum(a, b); }                                                                                TEST(test, driver_sum) {      EXPECT_EQ(320, driver_sum);      EXPECT_EQ(30, driver_sum); }2.3构建测试数据和测试代码遇到的技术瓶颈编写测试代码比较繁琐开发编写驱动不难,但是浪费大量的时间,并且没有创造性。编写代码是否有良好的代码规范单元测试也需要一些规范支持,例如代码规范,注释规范等,有了这些规范能够为测试人员提供很好的功能测试用例设计的逻辑思考,也为其他开发熟悉代码提供极大的便利。数据类型比较复杂通常情况下,输入参数比较复杂,嵌套层析结构比较深入,例如类包含其他类等。能否支持多组测试数据一般代码中每个循环、分支判断以及输入输出都有可能产生缺陷。因此单元测试需要很多用例,满足不同的条件分支,达到满足覆盖率。第三章 Wings的基本架构介绍3.1 Wings测试用例驱动自动生成技术的特性l  Wings是智能的全自动测试用例驱动构建系统,能够在很短时间内完成对大型复杂程序的自动解析、构建。每分钟达到100万行代码的生成速率。l  可以将任意复杂类型逐步分解为基本数据类型,例如结构体嵌套,复杂类对象,c++标准容器,自定义模板类等。l  支持多层次的可视化的数据表格来对变量类型进行赋值,无需关注驱动程序本身。数据表格可以表达任意深度和多层次的数据关系,用户只需要关注表格数据,完成对输入数据的校对。l  能够区分系统数据类型和用户自定义类型,对于复杂的约定俗成系统类型可由用户自定义扁平式赋值模板,例如std::vector类型等,内部集成常用系统类型的模板。3.2 Wings的技术架构介绍Wings的技术架构:首先Wings利用代码静态分析技术,提取被测程序的主干信息并保存到Program Structure Description(PSD)结构中,PSD结构中主要存储函数的参数、全局变量以及返回值的信息,类的成员变量,成员函数,以及访问权限等信息。利用存储的信息,依据一定的编写规则,自动构建测试驱动、googletest 期望断言、测试输入、参数捕获等代码和值。                                             图 3.2 Wings总体架构图上述Wings的总体架构图,说明了Wings构建代码与测试输入的具体过程,整个核心技术是通过编译底层技术获取程序的信息,然后按照一定的规则构建需要的数据。第四章 程序结构信息描述程序结构信息(Program Structure Description)是指对源程序进行提取后的描述信息,主要包括类名、命名空间、类的成员变量信息、类的函数信息,以及各种类型信息等(程序结构信息,下文简称“PSD”)。描述信息的保存C语言是以一个文件为单元存储,C++提取所有的类信息存储在一个文件中。Wings的主要特性是能够对复杂的类型(结构体、类、模板类等)进行逐层展开分解到最基本数据类型(char、int、string等)。PSD结构存储在XML文件中,不同的XML文件存储不同的描述信息。Wings提取的程序结果都存储在Wingsprojects文件下的funxml与globalxml文件夹中,其中funxml文件中主要存储的文件以及作用如下:RecordDecl.xml: 结构体,联合体与类描述信息。ClassTemplateDecl.xml:模板类描述信息EnumDecl.xml:枚举描述信息funcPoint.txt: 存储函数参数为函数指针的分析结果。funcCount.txt: 存储分析到的全部函数,参数个数,参数类型信息。void.txt: 存储函数参数为void*的分析结果。filename.xml:存储c语言文件的信息。注:具体文件的描述信息,参看附录A。针对复杂类型,例如结构体类型location_s,成员变量中除了基本数据类型之外,还存在包含结构体类型的情况,如下所示的代码中,location_s中包含coordinate_s结构体,以及FILE等类型的信息,针对不同的类型进行标记区分。 源代码如下:typedef struct coordinate_s{     void(*setx)(double, int);     int mInt;     char *mPoi;     int mArr[2][3];     void *vo; } coordinate;   typedef struct location_s {     int **mPoi;     coordinate *coor;     FILE *pf;     struct location_s *next; }location;   coordinate *coordinate_create(void); int coordinate_destroy(location *loc,size_t length,char *ch); void func_point(double param1, int param2); 生成对应的PSD结构信息如下图4:图4:PSD描述信息C++的主要表示类型是类,因此测试是C++以一个类为单元做测试,类主要包括类的成员变量名以及类型信息,成员变量的访问权限信息。类的成员函数分为构造函数、内联函数、虚函数等,成员函数的参数信息以及类型信息等。具体描述信息可以登陆www.codeWings.net下载Wings试用版本进行学习。第五章 Wings构建程序描述Wings通过获取到的存储信息,依据一定的编码规则,构建不同的代码程序,如驱动程序、期望断言程序、参数捕获程序等。5.1 驱动程序构建驱动程序指单元测试代码,Wings针对函数参数的类型,自动完成单元测试代码的编写。为了更详细的说明驱动程序,以一个C++类作为具体的例子说明。 类的声明如下:class BlockBuilder { public:     explicit BlockBuilder(const Options* options);     BlockBuilder(const BlockBuilder&) = delete;     BlockBuilder& operator=(const BlockBuilder&) = delete;     void Reset();     void Add(const Slice& key, std::string & value);     Slice Finish();     size_t CurrentSizeEstimate() const; private:     const Options* options_;     std::string buffer_;     int counter_;     bool finished_; };针对如上BlockBuilder 类,构建一个对应的驱动类DriverBlockBuilder,驱动类中主要包含构造函数、析构函数、每个成员函数的驱动函数以及返回值函数。为了避免类中重名函数,对写入PSD需要对应生成驱动的函数进行顺序编号。驱动类声明:class DriverBlockBuilder { public:     DriverBlockBuilder(Json::Value Root, int times);     ~DriverBlockBuilder();     int DriverBlockBuilderReset1(int times);     int Reset1Times;     int DriverBlockBuilderAdd2(int times);     int Add2Times;     int DriverBlockBuilderFinish3(int times);     void ReturnDriver_Finish3(class Wings::Slice returnType);     int Finish3Times;     int DriverBlockBuilderCurrentSizeEstimate4(int times);     void ReturnDriver_CurrentSizeEstimate4(size_t returnType);     int CurrentSizeEstimate4Times; private:     Wings::BlockBuilder* _BlockBuilder; };在上述驱动类中,构造函数的作用是构造BlockBuilder的对象,用构造的对象,调用测试BlockBuilder中的成员函数。析构函数的作用是释放构建的对象。 构造函数与析构函数:DriverBlockBuilder::DriverBlockBuilder(Json::Value Root, int times){   Json::Value _BlockBuilder_Root = Root["BlockBuilder" + std::to_string(times)];   /* options_ */   Json::Value _options__Root = _BlockBuilder_Root["options_"];   int _options__len = _options__Root.size();   Wings::Options* _options_ = DriverstructOptionsPoint(_options__Root, _options__len);   /* buffer_ */   std::string _buffer_= _BlockBuilder_Root["buffer_"].asString();   /* counter_ */   int _counter_ = _BlockBuilder_Root["counter_"].asInt();   /* finished_ */   bool _finished_;   int _finished__value_ = _BlockBuilder_Root["finished_"].asInt();   if (_finished__value_ == 0) {     _finished_ = true;    }   else {     _finished_ = false;   }   _BlockBuilder = new Wings::BlockBuilder(_options_, _buffer_, _counter_, _finished_, false); } DriverBlockBuilder::~DriverBlockBuilder() {     if (_BlockBuilder != nullptr) {         delete _BlockBuilder; } }每个成员函数对应生成自己的驱动函数。类中的Add函数对应的驱动函数如下。Add驱动函数:int DriverBlockBuilder::DriverBlockBuilderAdd2(int times) {     Add2Times = times;     const char* jsonFilePath = "drivervalue/BlockBuilder/Add2.json";     Json::Value Root;     Json::Reader _reader;     std::ifstream _ifs(jsonFilePath);     _reader.parse(_ifs, Root);     Json::Value _Add2_Root = Root["Add2" + std::to_string(times)];     /*It is the 1 global variable: count    Add */     int _count = _Add2_Root["count"].asInt();     count = _count;     /*It is the 1 parameter: key Add2      * Parameters of the prototype:const Wings::Slice &key */     Json::Value _keykey_Root = _Add2_Root["key"];     /* data_ */     char* _keydata_;     {         std::string _keydata__str = _keykey_Root["data_"].asString();         _keydata_ = new char[_keydata__str.size()];         memcpy(_keydata_, _keydata__str.c_str(), _keydata__str.size());     }     /* size_ */     unsigned int _keysize_ = _keykey_Root["size_"].asUInt();     Wings::Slice _key(_keydata_, _keysize_, false);     /*It is the 2 parameter: value    Add2      * Parameters of the prototype:const std::string &value  */     string _value = _Add2_Root["value"].asString();       //The Function of Class    Call     _BlockBuilder->Add(_key, _value);     return 0; }构成以上驱动函数的主要包括全局变量、参数、调用被测函数的构建。以上是针对BlockBuilder类的驱动类的主要信息,而构建的程序遵守google的编码规范。一些命名规则如下:Wings生成的驱动代码,存储在drivercode文件夹中。(1)driver.cc与driver.h,针对程序中使用到的一些公共函数以及头文件。(2)同一个结构体或者联合体,可能会作为多个函数的参数使用,为了避免代码的重复,Wings针对所有的结构体和联合体的不同类型,封装成不同的驱动函数或者参数捕获函数。driver_structorunion.cc 存储结构体驱动函数的代driver_structorunion.h 对应的头文件。结构体实现函数的命名规则为:DriverStruct+结构体名字+类型,其中Point代表一级指针或者一维数组,PointPoint代表二级指针或者二维数组使用。源文件的命名规则为:driver_+源文件名/类名+.cc例如:driver_nginx.cc 或 driverBlockBuilder.cc驱动函数的命名规则:Driver_+函数名+(编号)例如:Driver_ngx_show_version_info(void);DriverBlockBuilderAdd2(int times)    (3)返回值的打印输出返回值的打印输出函数命名规则:Driver+Return+Print_+函数名。例如:DriverReturnPrint_ngx_show_version_info();(4)用户源代码中的main函数自动进行注释,重新生成一个main函数文件,来进行测试。Wings会生成驱动main的主函数文件为:gtest_auto_main.ccWings主要针对参数进行逐层展开,解析到最底层为基本类型进行处理。驱动的赋值部分,主要就是针对基本类型进行处理。(注:特殊类型,比如FILE等,后面会详细讲解如何赋值)以int类型举例,一般程序构成int的主要组成大概包括以下情况:int p; int *p; int **p; int ***p;int p[1]; int p[2][3]; int p[1][2][3];int(*p)[]; int(*p)[][3]; int *(*p)[]; int (**p)[];int *a[]; int **a[]; int *a[][3]; int (*a[])[];Wings会针对基本类型的以上15种类型,进行不同的赋值。构建完驱动程序之后,要对驱动程序进行运行,Wings构建googletest的框架,来进行测试。下面我们将针对期望断言的googletest程序进行构建。5.2 googletest程序的构建在构建完单元测试驱动程序之后,Wings将调用googletest的框架,完成对返回值的期望值验证代码,并且输出测试结果。Wings运行单元测试过程中,会构建返回值的保存代码,将程序的返回值结果进行存储,然后读取用户输入的预期值,进行结果对比,判断是否通过。针对具体的类,对应生成gtest类,每个gtest类中对每个函数构建期望代码。例如针对BlockBuilder类,生成的gtest类为 GtestBlockBuilder。GtestBlockBuilder类的声明:class GtestBlockBuilder : public testing::Test { protected:     virtual void SetUp()     {         const char* jsonFilePath = "../drivervalue/RecordDecl.json";         Json::Value Root;         Json::Reader _reader;         std::ifstream _ifs(jsonFilePath);         _reader.parse(_ifs, Root);         driverBlockBuilder = new DriverBlockBuilder(Root, 0);     }     virtual void TearDown()     {         delete driverBlockBuilder;     }       DriverBlockBuilder* driverBlockBuilder; }; BlockBuilder类中的每个函数对应一个gtest函数,每个函数负责调用DriverBlockBuilder编写的驱动函数,对于包含返回值信息的函数,则对应生成具体的期望值对比。 期望对比函数CurrentSizeEstimate:TEST_F(GtestBlockBuilder, DriverBlockBuilderCurrentSizeEstimate4) {     const char* jsonFilePath = "drivervalue/BlockBuilder/CurrentSizeEstimate4.json";     Json::Value Root;     Json::Reader _reader;     std::ifstream _ifs(jsonFilePath);     _reader.parse(_ifs, Root);     for (int i = 0; i < BLOCKBUILDER_CURRENTSIZEESTIMATE4_TIMES; i++) {         driverBlockBuilder->DriverBlockBuilderCurrentSizeEstimate4(i);         Json::Value _CurrentSizeEstimate4_Root = Root["CurrentSizeEstimate4" + std::to_string(i)];         /* return */         unsigned int _return_actual = _CurrentSizeEstimate4_Root["return"].asUInt();         /* return */         unsigned int _return_expected = _CurrentSizeEstimate4_Root["return"].asUInt();         /* return_expected */         EXPECT_EQ(_return_expected, _return_actual);     } }最后调用自动构建的main函数运行整个单元测试过程。5.3 参数捕获程序构建参数捕获是指在程序运行过程中获取程序的变量信息,主要包括函数的参数、全局变量、返回值等。Wings自动构建获取参数的程序,利用插装技术,将构建的捕获函数插入源代码中对应的位置,将获取的具体信息,写入值文件,可以将获取的数据作为单元测试的输入,在代码发生变更后,利用相同的输入判断是否得到相同的输出,进行回归测试。Wings的参数捕获代码存储在paramcaputrecode文件夹中。其中命名规则同驱动格式一样,将所有的driver替换为param即可。Wings针对每个类生成一个对应的参数捕获类,而参数捕获类中针对每个函数生成对应的捕获参数、全局变量以及返回值的函数。c++ 中类的成员变量是私有,无法从外部获取,Wings利用插桩技术,对每个类插入一个捕获函数,来获取类的私有成员变量。参数捕获类ParamCaptureBlockBuilder:class ParamCaptureBlockBuilder { public:   ParamCaptureBlockBuilder();   ~ParamCaptureBlockBuilder();   void ParamCapture_Reset1();   void GlobalCapture_Reset1();   void ReturnCapture_Reset1();   void ParamCapture_Add2(const Wings::Slice &key, const std::string &value);   void GlobalCapture_Add2();   void ReturnCapture_Add2();   void ParamCapture_Finish3();   void GlobalCapture_Finish3();   void ReturnCapture_Finish3(class Wings::Slice returnType);   void ParamCapture_CurrentSizeEstimate4();   void GlobalCapture_CurrentSizeEstimate4();   void ReturnCapture_CurrentSizeEstimate4(size_t returnType); };具体的捕获函数不再详细说明,具体信息可以在Wings官网下载试用版本查看。第六章 Wings类型以及面向对象语法特性的支持Wings能够支持任意的类型以及面向对象的语法特性。类型支持:l  基本类型,int、double、float、std::string等l  任意复杂的结构类型,结构体、联合体、枚举、链表、多级指针、数组、树、图等l  任意复杂的类对象,标准库容器、自定义模板类特殊模板:n  区分用户自定义的类型与系统变量类型(标准库头文件中包含的类型)n  类的运算符重载函数n  void *与函数指针n  识别类中包含delete与default关键字的函数进行特殊处理语法支持:u  处理static函数、保护和私有函数u  类中定义私有结构体u  类中包含delete与default关键字的函数进行特殊处理u  多态与复杂的类继承6.1链表针对链表类型,采用比较灵活的赋值方式,考虑到实际应用中的一些因素,针对链表类型,默认赋值两层结构,在实际测试过程中,用户可依据需要自动添加节点。6.2 标准库容器Wings能够支持c++的标准库容器,能够对容器进行逐层展开,利用不同容器的标准赋值函数进行赋值以取值。其他类似的容器例如QT中的容器以及boost库中的相关容器,我们在继续支持。6.3 自定义模板类一些用户自定义的模板类类型,Wings能够识别是用户自定义的模板类,Wings依据实际程序中的赋值类型,进行处理。6.4  void*与函数指针Void*与函数指针在实际程序中可以作为函数参数或者结构体与类的成员变量,针对不确定的赋值类型,Wings提供了具体的解决办法:① 利用编译底层技术,对源程序静态分析,获取真实类型,对真实类型进行赋值② 由用户在数据表格界面配置实际类型6.5 特殊模板在实际的代码程序中,会存在一些类型无法使用通用的模式全部展开,如一些系统变量(FILE、iostream)、第三方库以及一些用户需要特殊赋值。Wings是如何针对以上特殊类型进行赋值,举例如下:struct sockaddr_in {         short   sin_family;         u_short sin_port;         struct  in_addr sin_addr;         char    sin_zero[8]; };步骤如下:a.        识别sockaddr_in为系统变量类型,特殊标记b.        检测程序中的所有系统变量类型,显示在模板界面c.         用户配置特殊变量,如sin_familyd.        构建sockaddr_in对象e.        调用模板,生成驱动模板配置如下:图:6.5模板配置  第七章 数据表格Wings目前测试用例数据采用随机生成的方式,支持int、char、double、float、bool、char*类型。数据表格可以任意编辑以上类型的数值。(1)Wings数据表格将会针对参数进行展开,假如参数类型为结构类型,数据表格将分层展开结构的类型,到基本类型。(2) 针对基本类型的指针类型,例如int *p;Wings处理为不定长度的一维数组类型,int **p;处理为不定长度的二维数组类型,默认长度为3,数据表格界面可以点击进行添加和删除数据。(3) 针对不定长度的数组作为函数参数,例如int p[];Wings默认长度为1,用户依据需求,在数据表格界面进行任意添加和修改即可。图 7-1数据表格展示 附录A表一:type属性ZOA_CHAR_S/ZOA_UCHAR/ZOA_INT/ZOA_UINT/ZOA_LONG/ZOA_ULONG/ZOA_FLOAT/ZOA_UFLOAT/ZOA_SHOTR/ZOA_USHORT/ZOA_DOUBLE/ZOA_UDOUBLE基本类型StructureOrClassType结构体类型ZOA_FUNC函数指针类型ZOA_UNION联合体类型ZOA_ENUM枚举类型ClassType类类型  表二:basetype属性BuiltinType基本类型ArrayType数组类型PointerType指针类型StructureOrClassType结构体类型UnionType联合体类型EnumType枚举类型FunctionPointType函数指针类型 表三:其他属性Name代表结构体、类、联合体名字NodeType代表链表类型parmType代表函数参数类型parNum代表函数参数个数SystemVar代表此类型为系统头文件类型value代表枚举类型的值bitfield代表位域类型所占字节returnType代表返回值类型Field类成员变量Method类构造函数paramName类构造函数参数名paramType类构造函数参数类型TemplateArgumentTypeSTL结构参数类型WingsTemplateArgumentSTL结构嵌套参数名字TemplateArgumentValueSTL结构中参数为具体值FunctionModifiers函数访问权限FunctionAttribute函数是extern或者static函数FuncClassName函数所属类OperatorFundecl重载运算符函数Operator重载运算符类型
总条数:210 到第
上滑加载中