对于某个特定程序,为其开发自动化
功能测试解决方案的过程,与创建该程序的过程,二者相较并没有很悬殊的差别。
自动化测试是一个非常年轻的领域,它正在不断经历大量的进步、提升和标准化进程。在这个领域中,涌现了许多与“被测系统”(SUT,System Under
Test)互动的新工具。
现在,
软件开发方面有大量可供选择的方法论和途径,例如:面向对象编程、函数式编程、领域驱动设计、测试驱动设计、行为驱动设计等等。它们拥有明确的声明性概念和理论,并简化了对初始系统架构的定义过程、对系统的理解以及开发者之间的知识交换等方面的
工作。
本文将主要针对GUI(图形用户界面)应用的测试自动化进行讨论——从自动化开发人员的角度看,在这种情况下被测系统(SUT)表现为一个黑箱(被测系统,是指一个正在测试是否能够正确操作的系统。对于桌面应用来说,它就是应用本身,而对浏览器系统来说——则代表了网站/Web项目等含义)。在公司的遗留系统占很高比例的环境里,或是在新开发的系统没有考虑可检测质量属性时,这一现象非常常见。
对最佳实践的准备和定义,是开发自动化的测试的关键部分。下图展示了被测系统和测试者之间的传统交互:
测试者与SUT之间的交互
位于该系统中心的,是一个扮演测试者角色的人类个体。测试者使用手动交互和应用的视觉化分析,以及特定的SUT非可视化界面访问工具,将
测试用例中所描绘的场景进行复制。如果失败或是遇到系统意外行为,测试者便将错误行为的有关信息输入到默认的追踪系统中。
自动化测试的主要目的,是消除(或者至少最小化)人类与SUT之间的交互。在持续交付产品开发周期中,这是非常常见的问题。一份文献来源的研究表明,现在自动化测试系统的数量非常多。商业产品一般会公布一系列详细的要求和推荐,特别适用于其产品。但是这些厂家往往不会公布适合与任何自动工具一起使用的一系列工具诊断实践。
此外,这些自动工具软件提供商往往还会运用营销手段,基于小量功能测试来描绘其系统的优势。但是随着自动化测试数量的增长,维护现有测试将成为系统工作流程中最昂贵的部分。
自动化测试框架旨在帮忙解决这些问题。他们定义了基本的系统可复用组件,公布了最佳实践和统一自动方法——为了正确地开发自动化测试框架,我们需要得到独立最佳实践的指导。
自动化功能测试的模式
让我们检查以下Web应用程序自动化方面的问题(图1),这是一个自动化解决方案装置的例子。该应用包含了一幅登录页面,而每项测试都必须经过该页面,才能进行更进一步的测试。
图1:示例——拥有最少页面和功能集的简单Web应用
分类体系(图2)为我们带来了全部功能测试模式的整体视角,本文稍后会对各个模式展开描述。
图2:自动化功能测试模式的分类
测试实现的模式
记录
通过自动化测试工具来执行此模式的测试实现。该工具记录并回放手工测试者的操作。某种程度上,考虑到昂贵的维护成本,这种方式被认为是一个糟糕的实践。
脚本化
由程序员使用自动化工具的API来执行测试实现(例如Selenium WebDriver API)。
实现基础模板(测试模板)
该模式将实现基础测试模板类。要实现各种测试的变体,可以通过继承和扩展模板类的功能来创建。
数据驱动实现
通过测试用例来定义一个基础测试的实现。可以通过一系列不同的输入数据组合,来创建各种测试的变体。
在大部分单元测试框架中都实现了这一方法,例如:MSTest以DataContext(键值集合)的方式,提供对测试内部的属性的访问权限。于是,相同的测试方法主体会运行很多次,但DataContext属性所提供的数据各不相同。
关键字驱动实现
这一测试实现借助了关键字(例如:点击、回车)。测试实现通过特殊的IDE完成,这类IDE可以钩挂到应用的UI上。
目前已有数款软件工具,支持借助关键字来实现测试。测试的步骤,以关键字、屏幕上的控制名和输入参数的结合的形式出现。HP QTP和MonkeyTalk是这类IDE中的典型例子
模型驱动实现
对任何应用来说,在特定的时刻接受特定的输入,都只会有一个特定的状态。根据这样的定义,我们可以将软件程序描绘为有限状态机(有限自动机)。考虑到这一事实,以及状态可用性和转换模型(例如图1),我们可以定义一套特定的页面间转换(工作流)的集合,它将能够覆盖程序的大部分功能。
架构模式
多层的测试解决方案
该模式从逻辑维度将正在测试的系统划分为独立的逻辑层级。
将软件系统以架构性方式分为独立的层级是一个广为流传的实践。第一层封装了表述逻辑,第二层则是业务逻辑层,而第三层负责数据存储。使用这一范式,有助于降低应用维护成本,因为更换每层内部的组件对其他层级的影响将被最小化。而同样的方法也可以运用到测试系统中。
测试代码同样可以分为三层:用来向SUT提供访问的UI自动化工具界面层、功能逻辑层和测试用例层。每一层都肩负特定的责任,而它们都在追求共同的目标——降低测试维护成本,提升创建新测试的便利性。
图 3: 架构性原型——测试系统的多层架构
元框架
该模式定义了一组基础的独立工具类。对任何自动化工具来说,它们都是通用的,而且可以在不同自动化项目之间复用。
当需要在一个组织机构中测试不同的项目,而且企业标准要求测试结果采用统一的界面时,或许就需要这样的解决方案。此外,元框架改进了项目间代码复用的度量标准,因为它可能会包含有用的工具方法。有关功能和测试对象的基础类,简化了项目之间知识的转移。元框架展现在图3的右侧。
功能组合模式
功能方法
针对特定于应用的业务功能,从其UI实现、API或其他层面进行了抽象。
用于自动化测试的许多工具,都支持创建被称之为“场景记录”的功能。当一位测试开发者针对特定应用执行特定操作时,这些工具将自动创建一份测试脚本。它可以在稍后进行回放,并检查在程序发生变化后,该脚本是否得到了正确的执行。
例子:改变登录界面的外观,将要求在全部预计的测试场景中的对应部分都进行变更。如果我们将登录方法提取为Application.Login(username,password),并在全部测试中使用这一方法,那么当登陆页面发生任何变更的时候,我们将只需要修改仅仅这一个功能方法,随后变更就会被自动分发到所有使用该方法的测试中。
图4:测试脚本与(a)缺乏功能方法的过渡层的用户界面之间的交互;(b) 带有功能方法层的用户界面。当应用发生变更时,阴影部分对象将会被改变。
页面对象
这一模式将某个页面的功能方法组合在一起。
由于图1中展现的用于应用的功能方法数量不多,因此它们可以移入一个单独的类。但是为了提升代码可维护性,此模式建议依据这些方法所代表页面,对其进行分组。例如,这些对应关系可以包括:页面:PageLogin——方法:Login();页面PageHome——方法:Logout()、CreateUser()。
功能库
它将某个特定应用的功能对象或(和)功能方法分组打包,成为一个适合复用的模块。
SUT的启动和拆卸对象(SUT运行者)
该模式支持被测系统的初始启动,即它的初始化。在此之后,测试对象释放与该系统相关的资源。
在功能方法之中,我们可以区分出与功能测试无关的方法集合:例如启动某个Web浏览器,并访问SUT的登录页面。而在测试之后,Web浏览器应该被关闭。SUT运行者负责以上这些通用活动。
对象源(对象之母、对象精灵、对象工程)
该模式按测试执行所要求的形式,创建并初始化的对象。
运输者(导航器)
根据测试要求,它将被测试系统中的导航控制集中在一起。
该对象封装了与被测试系统内部的导航实现有关的完整逻辑。因此业务逻辑的问题不会影响系统内的导航。
对于图1所描述的情况,我们将拥有一个Transporter类(运输者),它拥有以下方法:NavigateToLogin()、NavigateToHomePage()和NavigateToCreateuser()等等。或者,每个独立的页面(page)对象或许都会拥有自己的运输(transport)方法。在这种情况下,这些方法就作为该对象自身的运输者。
复合页面对象
该模式将复用的页面(page)对象聚合在一个外部对象中。
这一模式支持用更加“面向对象”的方式来组织页面对象——把可以在不同页面上复用的子对象分离,并将其包含到父对象中。
图5:通过在主页和创建用户页面对象中聚集,来使用导航页面(Navigation Page)对象
扩展的页面对象
该模式通过继承来扩展基础的页面对象,成为复合页面对象的替代选择。
过程模式
给定条件/时机/结果
此模式将测试执行过程分为三个阶段:
给定条件(定义先决条件)
时机(设定与上下文协同工作的特定操作)
结果(检查结果)
四阶段测试
此模式将测试执行过程分为四个阶段:
定义先决条件
调用业务功能
Checking results;
拆卸系统
流测试
该模式支持在一个测试内执行业务操作并进行检查。两项内容可以交替进行,直到实现测试的最终目标。
多次失败
该模式定义了一套机制,能够在非致命错误出现之后继续进行测试。
测试依赖模式
独立测试
该模式令被测试的系统回到测试前的状态。
链式测试
在该模式中,初步测试树立起了测试需要跟踪的SUT状态。
测试分组模式
每个测试类封装一个测试方法
在该模式下,每个独立的测试方法,都封装在一个独立的测试类中。
测试类中的分组测试方法
在该模式下,多个测试方法被放置于一个独立的测试类中。
总结
测试解决方案的设计背后的驱动力,是对特定测试实现模式的选择。它扮演了未来所有测试解决方案开发的起点,将在可读性、可维护性和其他诸多特性方面产生影响。而且它也设置了这些实验,使其在能够产生帮助的时候更好地复用项目之间的资源,并减少新项目自动启动的时间。
这篇文章抛出了有关如何参考设计模式,来构建测试解决方案的想法。
在以前和现在的
工作中都有过一些安全方面的涉猎,虽然不是很深入,也算是自己的一些经历,在接下来的几篇
文章中会逐步跟大家探讨安全性
测试,当然,更多是
web安全相关的。而这篇文章,就当作是说一些正确的废话,作为正文前的序。
白帽子与黑帽子
黑客并非都是黑的,白帽就是通过攻击自己的系统或者聘请来攻击客户的系统以便进行安全审查,与之相反,黑帽就是大家所熟知的“黑客”或“骇客”。他们往往利用自身技术,在网络上窃取别人的资源或破解软件。
另外,在白和黑之间,还存在灰帽和红帽,灰帽指的是那些懂得技术防御原理,并且有实力突破这些防御的黑客——虽然一般情况下他们不会这样去做。与白帽和黑帽不同的是,尽管他们的技术实力往往要超过绝大部分白帽和黑帽,但灰帽通常并不受雇于那些大型企业,他们往往将黑客行为作为一种业余爱好或者是义务来做,希望通过他们的黑客行为来警告一些网络或者系统漏洞,以达到警示别人的目的,因此,他们的行为没有丝毫恶意。
红帽其实就是以正义、道德、进步、强大为宗旨,以热爱祖国、坚持正义、开拓进取为精神支柱,这与网络和计算机世界里的无国界情况不同,所以,并不能简单讲红客就归于两者中的任何一类。红客通常会利用自己掌握的技术去维护国内网络的安全,并对外来的进攻进行还击,通常,在一个国家受的网络或者计算机受到国外其他黑客的攻击时,第一时间做出反应、并敢于针对这些攻击行为做出激烈回应的,往往是这些红客们。
我们经常都会看到新闻说某国家政府网站或军队网站被黑,这都是他们红客干的。最负盛名的一个事件就是2001年中美黑客大战,当时中国有8万多名红客对美国发起总攻,据网络新闻当时经过一天一夜的攻击,被攻陷的美国网站有90多个,被黑的中国网站超过600多个,由于国内一些红客没有把所黑网站及时报上去,中美被黑网站比例大致在3:1左右,而在当时,使用较多的就是实施DOS(denial of service)拒绝服务攻击。感兴趣的朋友可以问下度娘或谷哥了解更多。
安全攻击动机
一般来讲可以分为以下三种动机:
1. 经济利益,这也是绝大部分黑客所追逐的,而且在经济利益面前,非常容易被诱惑,白帽走上不归路,最后被法律所制裁;
2. 技术炫耀,特别是初学者都渴望练练手,做出点成绩给圈内人看,以证明自己,也许并无恶意;
3. 政治目的,上面内容已经详细介绍了这一类型。
黑客组织&论坛
国内一些黑客联盟包括:中国红客联盟,看雪学院,中国鹰派联盟,黑客基地,邪恶八进制。而比较知名的论坛:黑基论坛和Wooyun乌云论坛,后者每天都有很多人提交一些安全漏洞到乌云上,对
安全测试感兴趣的话可以经常上去看看别人找漏洞的思路,开拓自己的知识面。
具备的能力
真正要做好黑客或安全的人员实属不易,的确需要众多方面的能力:
较强编程能力
熟悉浏览器及其漏洞
熟悉编程语言及其漏洞
熟悉服务器及其漏洞
精通协议
熟练使用相关工具
使用JMeter代理录制脚本的过程如下:
1.启动JMeter,在
测试计划中添加“线程组”。
2.在“线程组”中添加“HTTP请求默认值”,参数设定如下:
3.在“”中添加“HTTP代理服务器”,参数设定如下:
4.在IE浏览器中“工具”—“Internet选项”---“连接”---“局域网设置”中设定参数如下:
产品的过程:用户- 用户研究-需求采集-需求分析-需求筛选-需求开发。
需求采集的方法:数据分析,调查问卷,用户访谈
需求采集的效果:尽可能多的采集
需求分析:听用户的但不要照着做 , 必须明确我们存在的价值.。
需求分析目的:把用户需求转化为产品需求
需求分析方法:给需求做一次DNA检测 来 确定需求的基本属性,分析需求的商业价值,初步评审需求的实现难度,从而计算出需求的性价比。
资源总是有限的,所以我们只能做那些性价比高的事情,活下来的永远是少数,少做就是多做,有意识的尽可能多的放弃。
从用户中来到用户中去
需求采集 过程:明确目标,选择采集方法,制定采集计划,执行采集,资料整理,然后进入下一步的需求分析阶段。
需求采集 方法:我们问 ,用户答; 用户说, 我们听。
需求采集 效果:用户量小, 时间多, 探究现象背后的故事。
用户访谈注意点:
1避免一组固定的问题:固定的问题会让被访者产生被审问的感觉,我们应该准备好问题清单,但清单只起一个引导作用,并不用照着读。
2 首先关注目标,任务其次:比用户行为更重要的是行为背后的原因,多问问用户为什么这么做。
3避免让用户成为设计师:听用户讲,但不要照着做,用户的解决方案通常短浅,片面。
4避免讨论技术:特别是碰到一些略懂技术的用户,不要与其纠缠产品的实现方式。
5鼓励讲故事:故事是最好的帮助设计师理解用户的方式。
6避免诱导性的问题:典型的诱导性问题是,“如果有xx功能,你会使用吗“,一般来说用户会给出毫无意义的肯定答复。
《软件观念革命:交互设计精髓》
几种方法:
定量的说:调查问卷
易出现的问题:
第一,样本的偏差,即样本与想了解的目标群体出现偏差。
尽可能覆盖目标群体类型的用户,性别年龄段 行业 收入等 要保证各类类型用户的样本比例接近全体的比例,比如目标用户中男女比例为7:3 那么样本中就保持这个数。
第二,样本过少的问题。百分比的数字只有具备稳定性才有价值
第三,问卷内容的细节问题。
1问题表述应无引导性。
2答案的顺序。可能产生“a顺序偏差”,“位置偏差”即别调差者的答案可能与改答案的排列位置有关。
问卷目的,样本对象,调查渠道,时间计划,问卷内容
可用性测试是指通过让实际用户使用产品或原型方法来发现界面设计中的可用性问题,同城只能做少数几个用户的测试,看他们怎么做,属于典型的定性研究。
可用性测试的常见问题与对策:
第一,如果用可行测试做得太晚(产品将要上线的时候),这时发现问题于事无补。
第二,总觉得可用性测试很专业,所以干脆不做。
第三,明确是测试产品,不是测试用户。
第四,测试过程中,组织者该做的和不该做的。
定量的做:数据分析
数据分析时,根据不同的目的,数据来源多种多样,常见的有用户使用产品的日志,客户管理系统里的信息,网页访问情况的统计信息等。数据分析的方法,最简单的可以用Excel,复杂一点的可以用一些统计软件,数据库软件,或者直接自己写程序解决。而最最关键的就是对结果的解读,通常数据分析只能发现一些现象和问题,并不能了解原因,所以分析完成后通常会伴随着一些用户访谈,听听用户怎么解释。
数据分析的常见问题与对策:
我们要意识到用户怎么做 和 怎么说 是不同的,甚至经常有矛盾,有时候用户的行为比语言更能反映出他的真实需求。比如用户说在搜索买家的时候应该加一个“按交易额搜索”的条件,也许只是他某次特殊的需求使然,但如果我们听他的做了这个功能,之后通过用户行为分析发现,只有1/10000的人用过,那就表明我们被用户的说法骗了,单数据永远不会骗我们。
数据分析时,有一些特定的问题:
第一,过于学术,沉迷于“科学研究”
科学研究通常只注重“性价比”的性,为了好的结果,往往不在乎投入成本,但是商业不是科学,更多的是对数据的敏感,对商业的敏感。
第二,虽然数据不会主动骗人,但是我们经常无意或有意的误读数据。
举个例子,对于一群人中,人们的身高用平均数是有意义的,那是我们知道他们符合正态分布,中间多两边少,所以一个平均值就能够了解群体的大体情况。而我们的收入并不符合正态分布,一个超级富翁和1000个零收入的人,平均下来每人100万,这是很荒谬的。解决办法:学习统计学的知识,努力提高自己的水平。
主动的误读数据,是比较有意思的事情。在提取数据之前,我们心中通常已经有了一些结论,无非是想验证它,而抱着这样的思想,就总能找到一些数据来证明自己已有的想法,并且技术越娴熟的人越容易做到这一点。对于这一点,我想一个简单的对策就是对数据保持中立的态度,尽量不要“为了迎合一个观点而去找数据”,减少利益牵扯,比如为了证明老板的判断,或是为了保持自己之前拍脑袋的英明形象等。
第三,平时不烧香,临时抱佛脚。
产品日志的商业价值
先做出方向性的假设,再提取相应的数据并分析,得到一些现象,最好是之前没发现的现象,然后尝试解释,接下来做用户调研修正解释,最终指导产品发展方向。
需求采集人人有责:
第一,生孩子和养孩子的区别。
第二,二手需求采集工具:单项需求卡片。
单项需求卡片:产品的需求工作不只是需求分析人员的事,而是涉及产品的每一个干系人的义务,至少得参与“采集”的过程。
表 2-3 单项需求卡片模板
需求编号(可由需求人员填写) 需求类型(可由需求人员填写)
包含“采集时刻 + 采集者”信息 功能需求、非功能需求等
来源(Who)(重要信息,方便追根溯源)
产生需求的用户:最好有该用户的联系方式等信息
用户背景资料:受教育程度、岗位经验,以及其他与本单项需求相关经验
场景(Where、When)(重要信息,用来理解需求发生的场景)
产生该需求的特定的时间、地理、环境等
描述(What)(最重要的信息)
尽量用(主语+谓语+宾语)的语法结构,不要加入主观的修饰语句
原因(Why)(需求人员要保持怀疑的心,很多时候理由是假想出来的)
为什么会有这样的需求,以及采集者的解释
验收标准(How) 需求重要性权重(How much):
(如何确认这个需求被满足了)
1. 尽量用量化的语言
2. 无法量化的举例解释
满足后(“1:一般”到“5:非常高兴”)
未实现(“1:略感遗憾”到“5:非常懊恼”)
需求生命特征(When) 需求关联(Which)
1. 需求的紧急度
2. 时间持续性
1. 人:和此需求关联的任何人
2. 事:和此需求关联的用户业务与其他需求
3. 物:和此需求关联的用户系统、设备,以及其他产品等
参考材料 竞争者对比
在需求采集活动中的输入材料,只要引用一下,能找到即可
按照“1 分:差”到“10 分:好”进行评估:
1. 竞争者对该需求的满足方式
2. 用户、客户对竞争者及公司在该需求上的评价
由于填写卡片的人经常不是专业的需求人员,所以卡片的质量无法保证,比如下
面这尽可能多的采集:
1现场调查 :打入敌人内部,和客户工作一段时间,深度了解需求。
2日记研究 :某个新产品出来以后,很多业内朋友去尝试,写一些体会。
3卡片分类法 :把产品的各种需求写在便利签上,让用户一起讨论并完成分类。
4自己提需求:看看别人的产品,总结很多问题,提出很多需求,让自己的产品越来越完美。
听用户的但不要照着做。
有些用户真的很危险,在提意见的同时还说你们应该做成什么样子,这时候产品经理一定要头脑清醒,用户的解决方案往往站在自己的立场上的考虑。
有时候用户的做法存在明显的逻辑矛盾,就算他给出的解决方案合理,也要在深度挖掘用户内心根本的需求。
我们是产品经理,产品设计师,最终怎么做应该由我们决定。
1明确我们存在的价值。
同一个问题的解决方案的区别,可能一个是用户需求,一个是产品需求,而在这中间的转化过程,就是这节的主题—需求分析。
用户需求VS 产品需求
用户需求:用户自以为的需求,并且经常表达为用户的解决方案。
产品需求:经过我们的分析,找到的真实需求,并且表达为产品的解决方案。
需求分析:从用户提出的需求出发,找到用户内心真正的渴望,在转化为产品需求的过程。
技术分析是“树干-树枝-树叶“的任务分解过程。
需求分析是“首先:树叶-树枝-树干,其次:树干-树枝-树叶“的分析过程。是一个分总分的过程。
一方面不能偷掉提炼用户需求的这个过程,目的是透过现象看本质,另一方面也不能停在本质上,所以我们还要继续把树干再重新分解成树枝和树叶。
伟大的需求分析师,可以无视用户想要的东西,去探索他内心真正的渴望,再给出更好的解决方案,或者是用户真正需求的东西,这就是本节标题的意思—我们存在的价值。
调查问题:你会为你想要的东西买单 还是需要的东西买单?
回答结果:女性:3都会 1个需要的必须买 有能力想要的会买 1个需要的买单
男性:1个需要的 1个需要的必须买 有能力想要的会买
销售人员经常说:“用户是为想要的东西买单,而不是需要的”,用我们上面分析翻译一下,其实是“用户是为自己提出的解决方案买单,而不是我们的解决方案”。
其实这是短期利益和长期利益的权衡,如果是一锤子买卖,卖出以后又不用售后,那么采用实用主义,不妨用户要什么就给他什么,这样他掏钱最爽快,你回忆一下在风景区买的纪念品的情景,大多数情况下,是不是你要啥卖家就给你啥?这种情况下就要追求短期利益。但是,我们的产品通常都是希望用户长期使用的,并且后续的服务也是我们来做,所以为了长期利益,我们就有必要找到用户的真实需求,然后给他真正合适的产品了,哪怕这个过程不那么讨好。不知道你有没有帮女生买电脑的经历,帮她买也就意味着将来的售后服务、技术支持、维修都是你了,所以你才会在型号和配置上和她争吵,努力说服她不要买那些中看不中用的,而要买“真正的需求”。满足需求的三种方式:需求源于理想与现实的差距,那么减少这个差距有三种方式:改变现状:最常用的,也是最笨的方法。降低理想:不要忽视精神的力量,什么“打预防针,丑话说在前头“这类句子相比大家都经常听到。转移需求:因为人类的注意力是有限的,所以引导用户去关注其他事物,他就会觉得这个差距没那么可憎了。我们也可以说,人的行为是需求驱动的,想改变人的行为,可以寻找更强烈的需求展现给他,而让他不再纠结于原来的需求
产品设计的最高境界---创造需求!
工作中典型的场景,就是老板或者产品人员的突发奇想,这些灵感在潜意识里都有一定的依据,是基于对用户、市场、产品的充分理解,也有过不少案例,这些需求最终获得了用户的认可,但更多的被证明是过于天马行空。苹果公司的乔布斯,可以说是创造需求的大师,但我不建议大家学,这是需要天赋的,但这份天赋非常值得保护,产品的进化和生物的进化一样,需要如基因突变一般的胡思乱想。更实际的,我认为需求分析的过程其实也有创造需求的成分,当一个新人真的能力不足的时候,不妨先做用户提出的需求,而不要自己去胡乱分析用户需求,而对于一个团队来说,要尽量避免“只有能力不足的需求分析人员”这种情况出现。
相关文章:
首先,你能快速回答下面的问题吗?
如果没有IDE(集成开发环境)的自动补全功能,如何获取数组和字符串的长度?
我问了不同等级的开发者(初级和中级)同样的问题,他们很难准确而确信地给出答案。虽然IDE提供的代码自动补全功能十分方便,但也带来了“表面理解(surface understanding)”的问题。在本文中,我会解释一些和
Java数组的关键概念。
上述问题的答案如下:
int[] arr = new int[3]; System.out.println(arr.length);//数组的length属性 String str = "abc"; System.out.println(str.length());//字符串的length()方法 |
现在的问题是,为什么数组有length属性而字符串没有?或者,为什么字符串有length()方法而数组没有?
问题1. 为什么数组有length属性?
首先,数组是一个容器对象,它保存了一定数量的某一类型的值。当一个数组被创建出来后,它的长度就不会再改变了。数组的长度可视作一个final实例变量。因此,长度可被认为是数组定义的一个属性。
创建数组有两种方式:1)数组创建表达式;2)数组初始化。一旦数组被创建,它的长度就固定了。
下面的数组声明虽然只指明了数组中第一维的长度,但它是合法的。
int[][] arr = new int[3][]; |
数组的初始化创建了一个数组并为其中的每个元素赋上初始值,它是由一个被“{”和“}”包围的用逗号分隔的若干表达式构成,示例如下:
问题2. 为什么没有一个像“String”一样定义的“Array"类?
因为数组是一个对象,所以下面的代码是合法的。
Object obj = new int[10]; |
数组包含了Object类中的所有成员(除了clone)。为什么不能像定义一个类那样定义数组呢?我们找不到一个Array.java文件。一种比较简单的解释是:它们被掩藏了。你可考虑一下这个问题:如果有一个数组类,那么它该是什么样的?我们还是需要一个数组来保存数组数据,是不是?因此,定义一个这样的类似很不明智的。
事实上,我们可以通过下面的方式来获取一个数组类:
int[] arr = new int[3]; System.out.println(arr.getClass()); |
输出的结果是:
”class[]“是”int类型数组“类对象的运行时类型签名。
问题3. 为什么字符串有length()方法?
字符串中保存字符数据的数据结构是一个字符数组。因此,没有必要定义一个在实际应用中不必须的字段。与C语言不通,在Java中,一个字符数组并不是一个字符串。
1. ASP与Access数据库连接:
<%@ language=VBscript%> <% dim conn,mdbfile mdbfile=server.mappath(" 数据库名称.mdb") set conn=server.createobject("adodb.connection") conn.open "driver={microsoft access driver (*.mdb)};uid=admin;pwd=数据库密码;dbq="&mdbfile %> |
<%@ language=VBscript%> <% dim conn set conn=server.createobject("ADODB.connection") con.open "PROVIDER=SQLOLEDB;DATA SOURCE=SQL服务器名称或IP地址;UID=sa;PWD=数据库密码;DATABASE=数据库名称 %> |
建立记录集对象:
set rs=server.createobject("adodb.recordset")
rs.open SQL语句,conn,3,2
3. SQL常用命令使用方法:
(1) 数据记录筛选:
sql="select * from 数据表 where 字段名=字段值 order by 字段名 [desc]" sql="select * from 数据表 where 字段名 like '%字段值%' order by 字段名 [desc]" sql="select top 10 * from 数据表 where 字段名 order by 字段名 [desc]" sql="select * from 数据表 where 字段名 in ('值1','值2','值3')" sql="select * from 数据表 where 字段名 between 值1 and 值2" |
(2) 更新数据记录:
sql="update 数据表 set 字段名=字段值 where 条件表达式"
sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达式"
(3) 删除数据记录:
sql="delete from 数据表 where 条件表达式"
sql="delete from 数据表" (将数据表所有记录删除)
(4) 添加数据记录:
sql="insert into 数据表 (字段1,字段2,字段3 …) values (值1,值2,值3 …)"
sql="insert into 目标数据表 select * from 源数据表" (把源数据表的记录添加到目标数据表)
(5) 数据记录统计函数:
AVG(字段名) 得出一个表格栏平均值
COUNT(*¦字段名) 对数据行数的统计或对某一栏有值的数据行数统计
MAX(字段名) 取得一个表格栏最大的值
MIN(字段名) 取得一个表格栏最小的值
SUM(字段名) 把数据栏的值相加
引用以上函数的方法:
sql="select sum(字段名) as 别名 from 数据表 where 条件表达式"
set rs=conn.excute(sql)
用 rs("别名") 获取统的计值,其它函数运用同上。
(5) 数据表的建立和删除:
CREATE TABLE 数据表名称(字段1 类型1(长度),字段2 类型2(长度) …… )
例:CREATE TABLE tab01(name varchar(50),datetime default now())
DROP TABLE 数据表名称 (永久性删除一个数据表)
4. 记录集对象的方法:
rs.movenext 将记录指针从当前的位置向下移一行 rs.moveprevious 将记录指针从当前的位置向上移一行 rs.movefirst 将记录指针移到数据表第一行 rs.movelast 将记录指针移到数据表最后一行 rs.absoluteposition=N 将记录指针移到数据表第N行 rs.absolutepage=N 将记录指针移到第N页的第一行 rs.pagesize=N 设置每页为N条记录 rs.pagecount 根据 pagesize 的设置返回总页数 rs.recordcount 返回记录总数 rs.bof 返回记录指针是否超出数据表首端,true表示是,false为否 rs.eof 返回记录指针是否超出数据表末端,true表示是,false为否 rs.delete 删除当前记录,但记录指针不会向下移动 rs.addnew 添加记录到数据表末端 rs.update 更新数据表记录 |
1:绝对装入方式(Absolute Loading Mode):
即程序在编译时就产生物理地址的目标代码,编译完成后,不在需要对程序和数据进行修改,程序员也可以在程序中赋值物理地址。
缺点:不灵活,要求程序员对内存相当熟悉,只适用于单道程序环境。
2:静态重定位装入方式(Relocation Loading Mode):
即程序在编译时使用的是逻辑地址,在装入的时候,临时将逻辑地址转换成物理地址。到内存中后的地址为物理地址。这种方法叫做静态重定位装入。
缺点:不灵活,当进程有需要动态改变地址时则无法改变。
3:动态重定位装入方式(Dynamic Run-time Loading)
即程序在装入内存之后,在内存中仍然使用逻辑地址,只有在取该数据时,才转换成物理地址。这样做会影响到程序的执行速度。因此,一般将转换成物理地址的过程做成硬件来完成转换,需要添加一个地址转换寄存器来支持。
二 程序的链接过程
1:静态链接(Static Linking)
程序在编译之后形成目标模块,然后将目标模块和库函数组合成一个模块,不再分开的链接。
缺点:消耗内存的资源很大,往往有的库函数或模块在程序的运行过程中没有使用,极大的浪费了内存空间。
2:装入是动态链接(Load-time Dynamic Linking)
程序在编译时形成目标模块代码,在需要调入内存时,链接成一个模块装入内存。
缺点:和静态链接一样,对内存消耗大。
3:运行时动态链接(Runtime Dynamic Linking)
程序在编译时形成目标模块,在调入内存时调入需要执行的代码模块,当用到某个库函数或模块式时,由操作系统动态的调入内存执行。
三 内存连续分配方式
1:单一连续内存分配
把内存区域中划分OS分区和应用进程区域两大块,互不干涉。在应用进程区域的内存按照单一的连续分配原则分配给各个进程。
使用范围:单用户,单任务的操作系统中。如CP/M,MS-DOS操作系统。
2:固定分区分配
a.固定分区大小一致分配
将内存划分为大小一致的多个分区,这种分配方式缺乏灵活性,当进程太小造成空间浪费,太大则无法分配空间,导致装入失败。
b.固定分区大小不一致分配
将内存划分为大小不一致的多个分区。这样可根据进程大小适当分配区域。
分配具体操作:将各个分区按照大小排队,用一张分区表存储,该表中包含分区的大小,起始地址,和使用情况
使用范围:IBM360的MFT
3:动态分区分配
根据进程的大小动态的分配内存空间,需要为其配置数据结构,分配算法,分区的分配和回收操作。
数据结构:
空闲分区表,用于记录空闲分区的大小,起始地址,分区序号等信息
空闲分区链,在每个分区的起始地址部分设置一个分区信息数据结构,包含前后分区指针,分区尾部设置分区状态和分区大小表目
分区分配算法:
a.首次适应算法(first fit)
在为进程分配分区时,从头到尾查找空闲分区表,若查找到分区的大小大于或者等于要分配的进程大小则为其分配该分区。该算法的倾向于利用内存中的低地址空间,而保留了大部分高地址空间
优点:为大进程留下了足够的空间。
缺点:产生很多内存碎块,严重浪费了宝贵的内存资源。
b.循环首次适应算法(next fit)
和首次适应算法不同的是,在查找到适应的分区之后,以后再次分配内存空间时,从上次查找的位置开始进行分配。
优点:使内存使用空间分布的比较均匀,减少了查找空闲空间的时间。
缺点:这样查找会缺乏大的内存空间
c.最佳适应算法(best fit)
将空闲分区表按照分区的大小从小到大排好队,每次分配空闲分区时顺序查找,找到适应的分区则分配进程,必然每次都是最佳分配。然而在宏观上不一定是最佳的,每次分配后都会留下难以利用的内存碎块。
b.最坏适应算法(worst fit)
将空闲分区表按从大到小的顺序排好队,依次扫描空闲分区表,每次分配给进程时,都将最大的内存空间分配之。
优点:每次残留的内存碎块都不至太小。对中小进程有利,对大进程最不利。查找效率高。
缺点:是内存中缺乏大的内存空间。
以上四种都称为顺序搜索法。
e.最快适应算法(fast fit)
该算法也称为分类搜索法,将空闲分区表中大小大体相同的分区单独放到一个空闲分区表中,这样在操作系统中就有多个空闲分区表,再建立一个空闲分区索引表,每个索引指向一个空闲分区表,每类空闲分区的大小可以按照2kb,4kb,8kb来划分。当要查找空闲分区时就可以通过查找索引来快速查找合适的分区。
优点:查找效率高,能保留大的分区,满足对大空间的需要,也不会产生内存碎片。
缺点:在分区归还内存时算法复杂,系统开销大,在分配时一个分区只给一个进程,或多或少存在一定浪费。而且在分区划分越细的情况下,浪费越严重。这是典型的以时间换空间的做法。
伙伴系统
伙伴系统是固定分区和动态分区分配的折中方法,在伙伴系统中,不论已经划分的分区还是没有划分的分区大小都统一为2^k次幂,k可以为1,2,3......m 内存的区域最小为2^1
最大为2^m次幂,也就是整个内存区域。在每次分配内存时就分配2^i次幂大小的空间,这样在多次分配后就会产生多个不连续的内存空间。将这些大小相同的内存空间分成一类,建立一个空闲分区链表,当要分配内存大小为m时,就比较2^i<m<2^i+1的大小,查找链表中是否存在2^i+1,若存在就将其分配给进程。若不存在就搜索2^i+2,若存在该大小的内存区域,就将其分成大小的为2^i+1的两个伙伴,一个用于分配给进程,一个加入2^i+1的空闲链表中。若没有找到,则依次类推。
在进行内存回收时,如果有2^i空闲区域则查找是否存在相同大小的空间,存在则合并为2^i+1,再搜索是否存在和2^i+1相同的空间若存在则再次合并。.若不存在,则加入2^i中。
伙伴系统在时间性能方面比分类搜索差,比顺序搜索要优胜。在空间性能方面则相反。在多处理机的系统中得到大量的应用。
一、页面链接检查:测试每一个链接是否都有对应的页面,并且页面之前可以正确切换。
二、相关性检查:
1、功能相关性:删除/增加一项会不会对其他项产生影响,如产品影响,这些影响是否正确(常见的错误是:增加某个数据记录后,如果该记录某个字段值内容过长,可能在查询的时候让数据例表变形)
2、数据相关性:下拉列表默认值检查(如果某个列表的数据项依赖于其他模块中的数据,同样需要检查,比如:某个数据如果被禁用了,可能在引用该数据项的列表中不可见)
3、检查“页面元素”是否显示正常
4、检查“按钮”功能是否实现(如:重置 按钮不能起到清空输入的作用)
5、输入项中类型的检查:在指定输入类型的地方输入其他类型(如 在 “电话号码”一列中输入字符型,系统是否正确给予提示)
6、边界值检查:规定某个输入项中最多输入50个字符,测试时,要测试输入50及>50个字符的情况
7、检查符号检查:输入的内容包括各种标点符号,特别是空格,各种引号,回车,看系统处理是否正确(常见错误是:系统对空格的处理,在增加的时候,将空格作为一个字符,而在查询的时候空格被屏蔽,导致无法查询到增加的记录)
8、特殊字符检查:输入特殊符号,如@、&、%、#等,系统处理是否正确
9、对输入中文字符的检查(有些系统的某些地方,可能对英文的处理OK,但对中文字符则出现乱码之类的)
10、检查信息的完整性:如更新某个记录时,是否将更新的所有信息都更新过来,还是只更新了其中一部分
11、信息重复:如名称、ID、空格、是否区别大小写等,系统是否给予正确提示(修改或新建)
12、检查删除功能:单条记录删除;多条记录同时删除;未选择任何记录,点删除或delete
13、必填项检查
14、上传文件的检查:文件格式是否正确;文件中某些字段是否允许为空;不允许为空的字段为空,系统是否校验不通过;文件中是否允许为空的记录;文件是否对记录中字段的长度、是否重复作校验;
15、快捷键检查:是否支持常用快捷键,如ctrl+c、ctrl+v、shift+选择、ctrl+选择、backspace等,对于不允许手工输入信息的字段,是否有做限制。
16、输入法半角全角检查
17、页面中显示的单位是否正确,如 获取某
数据库中值为分,到页面显示时未做转换,页面显示的单位为:元,其他如GPRS流量、数量等单位
18、文档的测试:文档二义性的确认、用户手册等
19、密码检查:一些系统的加密方法采用对字符的asccii码移位的方式,处理密码加密相对较为简单,且安全较高,对于局域网系统来说,此种方式完全可以起到加密的作用,但同时,会造成一些问题,即>128的asccii对应的字符在解密时无法解析,尝试使用”uvwxyz”等一些码值较大的字符作为密码,同时,密码尽可能的长,如17位密码,造成加密后的密码无法解析
20、用户检查:任何一个系统,都有各类不同的用户,同样具有一个或多个管理员用户,检查各个管理员之间是否可以相互管理,编辑,删除管理员用户。同时,对于一般用户,尝试删除,并重建同名的用户,检查该用户其他信息是否重现。同样,提供注销功能的系统,些用户 再次注册时,是否作为一个新的用户。而且还要检查该用户的有效日期,过了有效日期的用户是不能登录系统的。(可能出现的错误是:用户管理权限为非超级管理员,能够修改超级管理员的权限)
什么是等价类?
等价类:一类数据具有等价性。
从正向来说,它们具有相同的功能。
从逆向来说,它们暴露相同的错误。
有效数据->有效等价类 无效数据->无效等价类
如何划分等价类?
可以根据
测试数据背后的处理信息,分析数据有无共同特点。将含有共同特点的数据划为一个等价类。
等价类划分的原则
1、在输入条件规定了取值范围或取值的个数的情况下,可以确立一个有效等价类和两个无效等价类。
2、在输入条件规定了输入值的集合或者规定了“必须如何”的条件的情况下,可以确立一个有效等价类和一个无效等价类。
3、在输入条件是一个布尔量的情况下,可以确立一个有效等价类和一个无效等价类。
4、在规定了输入数据的一组值(假设N个),并且程序要对每一个输入值进行处理的情况下,可以确立N个有效等价类和一个无效等价类。
5、在规定了输入数据必须遵守的规则的情况下,可以确立一个有效等价类(符合条件)和若干无效等价类(从各个角度违反规则)。
6、在确知已划分的等价类中各元素在程序处理中的方式不同的情况下,则应再将该等价类划分为更小的等价类。
划分完等价类以后就要设计用例对划分的等价类进行覆盖。对于有效等价类,要使用例能尽可能多地覆盖尚未被覆盖的有效等价类,对于无效等价类,则每次只覆盖一个。
等价类划分的优点是比较简单,缺点是它并没有考虑组合的情况。
引言
Hadoop集群的计算和数据处理能力随着集群规模的增长逐渐形成了一个弥漫天际的浩翰空间,处于其中的各种数据应用、采集作业、数据分析、数据挖掘,以及前沿的机器
学习、人工智能等都如同空间中的一朵朵云彩,此消彼长。Hadoop集群根据业务提起的请求按需动态分配计算资源、数据空间,虽然业务的需求是复杂多变的,但是对于大规模的Hadoop集群来说,整体的计算能力需求则始终是平滑的。这正是
云计算的特点,而为了应对这样一个动态的计算资源,仅仅通过前几弹描述的一些含有相当强烈针对性的
测试作业来模拟真实状况显然是远不够完备的。
因此,我们需要对每一个新发布的Hadoop版本进行真实的业务线作业仿真模拟回归。这种仿真回归可以最大限度的验证发布版的每个功能点是否正确可靠,检查在新版本下集群的稳定性、数据的正确性和完备性,以及运行周期是否出现变化,是否引发了新的计算热点或出现数据倾斜。本身仿真模拟回归也一直是发现bug最多的一项测试活动,通过业务线仿真回归验证的新版本,就像盖了最后一道质检认证章,能给予用户、运维和开发团队更好的上线信心。
常态化回归
业务线的回归按照规模大小来分,可以分为常态化回归和定制化回归。常态化回归一般用于小版本发布前的集成测试,作为测试周期的一部分,需要快速、简洁的定位出常规问题。因此这种类型的回归规模一般都不大,选取的业务作业往往具有广泛的代表性。此类业务作业所需的数据可以自行创建,用非真实的随机数据模拟填充业务数据,但作业内容可以是直接抽取线上的真实执行过程来模拟。业务完成后,通过自动化工具将新生结果与基线结果进行全量对比。
我们在云梯的测试中,有一个早期引入的业务线回归环境,数据和作业都比较陈旧,但是在广度覆盖面上是比较充分的,基本覆盖了广告线、搜索线和BI线三大模块。业务回归由调度系统、执行gateway和结果对比三个部分组成,执行过程中收集指标信息,判断任务执行过程的正确性,以及判断对比结果是否出现偏差。早期这个业务线都是手动执行各步骤,然后人工分析监控指标数据和对比结果,过程冗长易出错,后来有了第三弹提及的DST系统后,通过自动化工具将各步骤顺次串联执行,终于实现了业务回归一键自动化。测试人员只要坐在页面前点点鼠标,等待一段时间后看一下结果就可以了。
当然,业务线的引入和各模块的部署安装以及各步骤的理顺并非一蹴而就的事情,其间复杂度是很高的,这也是在跨机房项目之前一直沿用这个比较老的业务线做常态化回归的原因。也正是因为跨机房项目的复杂性导致沿用近两年的老数据和老作业逐渐不具备广泛的代表性,借此契机我得以了解了整个业务线仿真回归的设计过程。下面将就此进行介绍。
数据快照
既然要跑真实的业务线作业,那么就少不了需要从线上集群导入脱敏后数据(后文会提及从安全角度如何做脱敏)。显然,实现海量数据的全量模拟回归是不现实的,因为这将需要建立一个和线上集群同等规模的测试集群来做这件事情,耗费成本巨大,更体现不了测试团队在这个过程中的技术价值。当然灰度发布是一个可行的方案,但这种方式要求线上集群必须停止一段时间的对外服务或者腾出部分空间和计算资源,专门用来对新版本进行验证,对于24小时不间断且基本处于满负荷状态的云梯来说,是没法提供这样的机会来做灰度发布的。
既然不能导入全量数据,也不能通过灰度发布来模拟业务线回归,那么就只能通过导入某一天的数据来实现我们的测试目标了。于是我们在启动新的业务线设计流程后,立即开始将最近的一天数据导入到测试集群。之所以取最近的一天,是因为一般说来为了提高空间的利用率,节约存储资源,数据在结果表生成之后最快3天左右就会出现大规模的合并和删除,导致旧的原始数据出现缺漏。因此这个快照不仅要取最近的一天,还要争分夺秒的从线上拖拽到测试集群,以防止时间过长后数据被合并或删除(后面会谈一下原始数据已经缺失后怎么办)。
数据快照的取得需要从audit日志中进行分析,看前一天晚上生产作业都访问了哪些数据,好在HDFS的这些信息都是导入到hive中的,可以通过sql语句快速查询整理出这个列表,刘顺提供了这个列表,我们循惯例称呼为“刘顺列表”,这和跨机房之后的刘顺列表还是有点类似的。顺带说一下,顺哥一直是各种列表的产生源,因为执着所以专业。
这个列表并非完整的,在后面我们的实际调试过程中,不断发现某些作业需求的数据有缺失,因此需要动态的补充数据。跨机房项目中,我们启动了两个集群做业务线的仿真测试,其中一个用于固定的BI线回归,提取了2013年5月19日的数据快照,执行作业也是固定不变的,因此数据的补充是一个短暂的过程,短时间调试后,这条业务线就可以正常的
工作了。而另一个集群,则汇合了几乎所有应用云梯的项目组提交各自的作业在其中进行模拟回归,这个集群更合乎线上的真实状况,用户不断提交各自的真实业务作业到云梯上进行测试,因此数据也需要经常动态补充,为此,我们这边在dst上快速开发一个页面提供给用户提交各自所需的数据文件路径,我们拿到这个信息后,就会尽快进行数据补充。开始都是人工通过distcp来拖拽数据,后来也快速迭代了自动化工具,配合简单的人肉甄别(感谢慧觉的不懈努力)实现了几乎无缝的数据补缺工作。这个集群的数据量从早期不到600TB,一直补缺增长到了1.2PB,前后一个多月的大规模业务回归模拟中,通过这个集群发现并解决了很多云梯跨机房产品的问题,其价值不可估量。
除了HDFS上的数据快照,还需要获取业务线的meta数据快照,这包括2013年5月19日当天的hive元数据信息,以及作业执行脚本等等。这些meta数据,在线上也是日新月异的,好在量并不大,在业务方的配合下,我们快速搭建了一台gateway用于BI线业务回归。而另一个多项目组配合的集群则也是模拟线上的真实情况,几乎每个项目组都单独配置了一个gateway,最终配置了16台gateway。
一切源于真实
数据快照取得之后,云梯还需要打通各类权限,以模拟线上的用户和组,同时又不能让这些用户和组无意中访问到线上,干扰了正常的生产作业。因此Namenode和Jobtracker上的用户组配置被完整的拷贝到了测试集群,但是对密码都做了修改,只保留了一个账号用于补充数据所需。测试集群所有机器都修改了/etc/hosts列表,将namenode和jobtracker地址指向了测试集群的这两个角色,测试作业所在的gateway无需任何改变就将作业跑进了测试集群。这期间也出过一次不大不小的故障,部分作业因部署的gateway疏忽修改而跑到了线上,好在密码不对,所有操作都被线上集群拒绝了。这样的双重保障最终确保了测试与线上之间的安全隔离,两者互不干扰。
除了确保数据的真实性之外,我们也同样完整的复制了调度系统“天网”,在光锐团队的大力配合下,完整的业务线作业最终成功执行。天网遵从下面四个条件调度任务作业:
1.节点的运行日志为空
2.节点存在运行失败日志
3.所有依赖的父节点已运行完毕,且状态为运行成功
4.该节点没有被人为设置成不执行(有些作业因为受环境限制不得不停止调度)
因此我们可以通过清空日志来重新调度我们需要执行的业务线作业。由于测试集群性能比不上线上,因此调度作业的先后顺序会由于执行时间不同而与线上相比发生变化,有些依赖关系上的bug也在本次模拟回归中暴露了出来。
为了真实的模拟跨机房全过程,云梯测试在业务线仿真回归中,同样仿真了跨机房全过程,可以说业务线测试集群的参与人员是跨机房项目真正的先锋部队。业务方项目的测试人员是第一批真正感受跨机房的群体。从众多客户端到云梯客户端的统一,从过去直连Namenode到启用viewfs通过zookeeper选择Namenode,从单一Namenode到federation版本的Namenode一切为二,从Datanode向一个Namenode汇报到向多个Namenode汇报,从两个Namenode在同一机房到其中一个Namenode迁移到新机房,从数据的不跨到跨机房,从副机房的副本的增加到主机房副本减少,从主副机房的数据独立到crossnode启用,业务线仿真回归集群紧密跟随着跨机房复杂而又零碎的脚步前行,确保每一次修改上线都经过了周密的验证,盖上了由云梯测试以及众多业务方项目测试团队确认的权威“质检章”。
流程图
为了更直观的说明业务线仿真回归的全部设计过程,我抽象了如下图所示的流程图,取复杂的多业务线回归集群为例:
整个过程中,云梯测试团队处于一个组织者的角色,Owner了整个项目的顺次执行,同时成为了测试集群的运维和技术支持,保障了业务方项目组测试人员可以顺畅执行各自负责的部分。同时还参与到了具体业务中,帮助业务方调查问题,甚至应业务方要求搭建了一些微型云梯集群供其执行一些非常特殊的测试。
如今跨机房项目成功上线后,回忆起那段与业务方团队浴血奋战的峥嵘岁月,虽然艰苦繁忙,但是也收获良多,这种非常难得的大型业务线仿真回归实践,更是让我站在了一个比较高的层面上得以一窥云梯这个数据海洋的洋流全貌。
正确性验证
BI业务线是一个相对来说,输入输出以及执行脚本都比较单一的以hive为基础的仿真回归测试。既然是测试就必然需要做正确性验证,我们在这个过程中,沿用以前花香设计的对比脚本的思想,全新制作了全量对比工具。在BI线执行中,我们在gateway上的hive安装了一个hook,将所有新生成的表和数据同时生成一份到我们指定的位置(这个hook也用在多业务测试集群中)。在使用老版本hadoop测试完毕后,我们将自己设定的输出路径改名保存,然后重新部署新版本再次执行BI业务线回归。这样就获得了新老两次输出结果。和BI业务方讨论出需要执行全量对比的重要表列表后,全量对比自动化脚本就会生成两张与原始表schema完全相同的新表,然后将新老两次输出的路径作为location赋值给这两张新表。再利用hive sql对这两张表执行全量对比(具体sql因涉及安全不便透露分析)。
对于其他业务项目来说,一般各自团队都有验证正确性的方案,在这次跨机房业务仿真回归中可说是八仙过海各显神通。但占据极大比例的hive相关作业都是通过上述方式进行的正确性验证。
除了结果的正确性,执行过程的正确性和完备性也是需要进行验证的,这主要由审核各类执行日志来进行判断。此外,由于涉及客户端的统一化,因此兼容性测试的执行过程正确性尤其被关注。在跨机房项目的后期,稳定性测试的7*24小时连续不间断高负荷运作过程中,也不断需要对结果和执行过程的正确性做校准。本身,正确性验证由于全量对比造成的高负载也被作为稳定性测试的一部分被不断重复执行。至于性能和指标数据的正确性则由监控系统来保证,跨机房项目不仅需要集群内的监控,还需要多机房间的网络监控,以及主机房节点与副机房节点之间数据传输的监控,在有针对性的部署了多套监控体系后,我们甚至可以通过不同的监控系统来对同一个关注指标进行证伪性质的正确性判断。
一些大家关注的问题
1.数据脱敏
从线上直接引导数据至测试集群,鉴于测试集群的参与人员较多,数据安全就成为了一个比较重要的问题。虽然同样有权限方面的控制和操作方面的审计监控,但有心人在线上不可能进行的诸如数据补全、对比等执行过程中,总可以绕过防范来抓取到敏感数据。这便是数据脱敏的意义所在,根据原始数据的特点,我们通过随机字符的填充,或者MD5码等方式来加密数据,并在加密过程中加上唯一的seed,来确保数据之间的关联不会因此发生断裂。
2.缺失的数据
前文提到数据的时效性,部分作业会在结果报表产生后立即就对源头数据进行清理或合并,这导致我们拷贝来用于做业务回归的数据不完整。此外,大部分源头数据在3天或一周后都会进行删除合并操作以提高空间使用效率,而我们的数据快照始终必须是那固定的一天,当这一天的数据缺失后,我们通过拷贝最新的数据然后改写这些数据的日期来达到补全源头数据的目的,完美的解决了缺失数据的问题。
3.随机数据的全量对比
业务线作业的类型是复杂的,其中有一些机器学习算法或者和日期、计数器相关的数据会在每次回归后都出现全量对比不能通过的现象。例如包含几百万条根据生日确定年龄的表中,由于作业运行日期的不同,哪怕只是隔了一天也会出现其中的365分之一的年龄数据出现变化。因此遇到这种数据,需要采用根据字段特点来进行匹配的方式做全量对比。而对于完全受随机数影响构成的一些数据,则只能忽略进行对比。好在这种随机数据在整体中的占比非常小。
总结
终于到了系列文章的尾声,本文作为系列文章的最后一弹,不仅是因为其复杂性较高,考验了整个跨机房项目参与人员的组织能力、技术能力以及协同合作的能力,且时间跨度较长,横亘整个云梯跨机房项目开发周期,更因为业务线仿真回归汇集了大量各种各样的不同项目的自动化工具,以及人工的协调参与,可谓自动化与测试工具之集大成者。鸟览云梯跨机房项目始末,无论从宏观角度还是从微观角度来看,业务线仿真回归测试都和整个跨机房项目一样气势恢宏、动人心魄。这是无数测试团队与技术人员之间的一次共舞,是合作与竞争、组织与协调、默契与分歧的一次盛会。置身其中,感受无数技术与思想的碰撞,无数创新与灵感的火花,成为一个里程碑一段历史的见证者,我深感无比的荣幸。谨以此文献给这一段历史,期待在将来诸如登月等大型合作项目中可以再次实践。
3