需求是整个软件项目最关键的一个输入,据统计,不成功的项目中有37%的问题是由需求造成的。和传统的硬件生产企业相比较,软件的需求具有模糊性、不确定性、变化性和主观性的特点,在硬件生产企业中,产品的需求是明确的、有形的、客观的、可描述的、可检测的,而软件需求不具备此特征。需求文档作为客户和开发人员、开发人员之间进行交互的文档,它将系统的需求进行了“固化”,是需求的载体,其作用是至关重要的。笔者结合多年的企业管理信息系统的开发经验,总结了如下的需求描述的方法与经验,供各位同行参考。
1 构成企业管理信息系统的5个基本要素
对企业需求的描述可以从2个方面来进行描述,一个方面是对客户现行系统的描述,一个方面是对系统未来的设想。总的而言,无论是从那个方面来描述,构成企业信息系统主要包括5个基本要素:企业的组织结构、流程、数据、商务规则与功能(性能)。其中从用户的角度主要关注流程,是以流程为核心的,通过流程将其他几个要素贯穿起来,需求分析人员也应该从这个角度来和用户沟通;从开发者的角度主要关注企业的数据、商务规则与功能,以便于系统的实现;从实施者的角度主要关注企业的组织结构与功能,以便于系统的发布与实施。
(1) 企业的组织模型
即企业的组织结构关系,包括部门设置、岗位设置、岗位职责等。树型组织结构图是描述企业的组织模型的一种常用方法,它可用来搞清各部门之间的领导关系,每个部门内部的人员配备情况, 职责分工等情况,它是划分系统范围,进行系统网络规划的基础。在组织结构图中应将用户的组织结构逐层详细描述,每个部门的职责也应进行简单的描述。组织结构是用户企业业务流程与信息的载体,对分析人员理解企业的业务、确定系统范围具有很好的帮助。取得用户的组织结构图,是需求获取步骤中的基础
工作之一。
用户环境中的企业岗位或角色,和组织机构一样,也是分析人员理解企业业务的基础,也是分析人员提取对象的基础。
对用户角色的识别常常遗漏的是计算机系统的系统管理人员,角色识别不全,对以后的功能识别会造成盲区。
(2) 企业的流程模型
即企业的业务流程,包含哪些流程、流程之间的关系、每个流程中包括哪些活动、每个活动涉及到的岗位。企业的作业流程首先要有一个总的业务流程图,将企业中各种业务之间的关系描述出来,然后对每种业务进行详细的描述,使业务流程与部门职责结合起来。详细业务流程图可以采用直式业务流程图形式。对企业而言需要定义关于业务流程图的描述标准,大家采用相同的图例来描述,便于管理。
业务流程图的优点 :
■绘图的过程,实际上是作业流程条理化的过程
■表达形象直观,易于和用户交流,易于项目组内部交流调研的结果,需要得到用户的认同,这就需要和用户交流调研的结果,交流的文档要通俗、易懂, 不能采用专业术语。
■可以作为培训实施人员与技术服务人员的文档
业务流程图的缺点 :
■对高层管理人员的实际需求调查的不清楚.
这一方面是由于用户没有接触过计算机, 对采用计算机后的管理会是什么样子?计算机能够完成当前手工操作的哪些内容?能够作哪些现在手工无法完成的工作等等没有清楚的概念,因此用户无法将这些问题反应出来. 另一方面说明分析人员没有经验,对原始材料挖掘不深,不能从用户 提供的材料中提炼处来用户的真正需求,不能找到当前管理中的问题。
■对各种业务之间的总体关系没有表达出来.
采用直式业务流程图可以将企业的每一种业务的处理流程清楚地表达出来, 但是各业务之间的联系却没有表示出来,单看一种业务的流程图很清楚,但是却不能综合在一起,没有整体的概念,作为需求分析的文档,在这方面表达的不够完整。
■在不利用工具的情况下,画法烦琐。
图形可以将流程描述的很清楚,但是还要附加以一些文字说明,如关于业务发生的频率、意外事故的处理、高峰期的业务频率等,不能在流程图中描述出的内容,需要用文字进行详细描述。
(3) 企业的数据模型
即企业中的信息载体有哪些?以及对这些信息载体的详细刻画,包括企业的各种单据、帐本、报表的描述。在需求报告中,应该将单据的描述格式化,需要描述的内容包括:
单据的用途,即单据用在什么地方?
单据的格式:需要明确的画出来,并有实际的有数据的样例,能够具体直观地说明问题;
单据中的数据项的具体描述:长度、类型、计算生成方法、约束条件等;
单据的数据项是由哪些不同类型的角色来填写地,包括用计算机可以填那些数据项。
单据中哪些数据是必填的,哪些是可以不用填的。
单据流量:平均每天产生多少条记录,高峰期的数量;
单据的分类:可以从多个角度上进行分类,如:按业务类型来分类(采购/销售/生产),按生成的方式来分类(手工录入型/自动生成型),按格式变化的频繁程度来分类(易变型/稳定型),按表现形式来分类(列表型/卡片型)等等。
单据之间的关系:引用关系等等。
同样对于需要的报表与帐本也可以参照上面的条目进行详细的刻画。
(4) 企业的商务规则模型
即企业中的商务规则有哪些?这些规则用在哪些地方? 商务规则可以从影响的范围划分为2类:一类是局部的规则,如不允许出现负库存,一类是整体的规则,如对所有的物料管理到批次。商务规则一般是隐藏在功能模型或者流程模型中,不需要单独描述,但是有些复杂的商务规则是需要单独抽取出来描述,如企业的各种单据记帐的商务逻辑,5)企业的功能模型
功能需求是用户的最主要的需求,对用户功能需求的描述可以采用文字描述也可以采用语言加图形的描述方式,只要能够将用户的需求描述地完整、准确、易于理解即可。对功能需求比较复杂的系统(如超过10个功能项),可以先描述一个概要,对简单的系统可以直接进行详细描述。对于用户的功能需求要进行分类,分类的方法应便于用户理解,如按照用户的部门设置情况,进行描述每个部门的需求,这样也便于组织用户进行评审。以下是分类方法的举例:
按部门分类:如采购科、销售科、计划科、生产车间、财务科、统计科、总经理等;
按功能类型分类:如单据录入、单据审核、单据查询、记帐、帐本查询、统计报表、系统维护等。
对功能需求的分类在不同的层次可以采用不同的方法。
对每一项功能应有一个功能编号,以便于与功能规格说明书中的章节进行对应。对每一项功能的描述,应指明用户的输入(input)、处理方法(process)、系统的输出(output)及对此项功能的其他要求。功能需求还应注明使用此功能的岗位。对系统管理员要求的特殊功能可以在此注明,非特殊要求可以在需求分析规格说明书中详细论述。如用户权限可分级,要有操作日志等。
功能需求与性能需求是密不可分的,笼统的性能需求没有任何意思,必须具体到某项功能需求上来,这是分析人员在分析系统时容易忽略的一项。
对上述的5个基本元素可以将他们描述为一个五元组〈组织,流程,功能,数据,业务逻 辑〉,对于用户来讲,他们习惯于从组织维来看待系统,即某个部门有哪些岗位,每个岗位参与了哪些流程的哪些活动(功能),在某个功能上操作了哪些数据,对这些数据进行了哪些逻辑处理;对于开发人员习惯于从功能维来看待系统,即某个功能操作了哪些数据,对这些数据进行了哪些逻辑处理,这个功能属于哪个流程,可以由哪些岗位来使用;对于设计人员可能习惯于从数据维来看待系统:即系统中有哪些数据,在这些数据上可以做哪些处理,这些处理用OO的思想来看即是对数据对象的操作。
对以上的5个基本元素进行描述实际上就是系统建模的过程,为确保模型的可操作性,除了上面的5个基本要素外,还需要重点描述的内容有:
(1) 新系统对应用模式带来的变化
包括对企业的组织结构、作业流程、单据帐本报表等的格式、商务规则等的改变。
(2) 新系统的界面模型
用开发工具将用户操作界面快速画出来,使用户心中有数。若时间允许,可将界面原型与数据库表、字段连接起来,真正做出系统雏形,即快速原型法。
2 阅读需求文档的4类读者
需求报告的最终目的是给人来阅读的,所以一定要考虑需求报告的读者群,有4类角色可能阅读企业管理系统的需求文档:
客户与用户业务高层;
用户的中层管理人员与具体人员;
用户IT主管与开发人员,包括设计人员、编码人员、同行的专家;
项目管理人员:包括项目经理、质量保证人员、测试人员、需求管理员、配置管理员、计划人员等等;
不同的读者对文档的阅读需求是不同的,他们关注的信息是不同的。我见过了很多次需求评审的失败(如果做好需求评审我会另外再撰文描述),总结下来我认为和需求描述没有区分读者群是很有关系的。针对上述的4种分类,我们具体的来分析一下每类读者的特点:
(1) 客户与用户业务高层
他们关心的企业是系统的目标性需求,关心的是系统总体的功能框架,关心的是系统解决了哪些管理问题,对具体的需求是不关心的,所以给他们阅读的文档应该是从总体上来描述,要高度抽象。由于他们的工作很忙,很难有比较长的时间来读这些材料,所以要简短明了,能够用1页纸说明问题的就要不要用2页纸,而且一般都要给高层进行需求汇报,需要配上语言说明,因此采用PowerPiont片子也就成了一种常用的方法,讲解需求与讨论一般应掌握不要超过1小时。需求人员常犯的毛病是过多地关注了企业的细节性需求,而忽略系统的目标性需求,所以在安排需求获取的步骤上、需求报告的编写上往往没有抓住企业高层最关心的问题、没有抓住根本性的问题,在给企业的高层汇报时当然很难通过评审。
(2)用户的中层管理人员与具体人员
企业的中层管理人员关注的是企业的局部需求,他们要求对自己的负责的局部系统能够有总体的了解,能够和其他的子系统衔接的很好,业务流程很流畅,覆盖了自己需要的所有业务流程,能够通过系统起到控制作用就行了。具体的操作人员更关心自己的的哪些活动是否在系统中都能处理,软件是否可以很容易地操作,他们关注的焦点更具体,要求更直观。所以对这类的读者可以通过比较详细的文档来描述需求了,当然应该以他们习惯的思维方式来描述,不能从开发人员的角度来描述。我看到过很多几百页的需求文档给用户去阅读、去评审,结果要么用户不置可否,要么直接讲看不懂,为什么呢?一是开发人员在文档中分子系统、分模块、分功能点一层深入下去描述,不符合用户的思维习惯,他们希望能够从业务流程、业务活动的角度来考虑问题,而不是功能;二是太多了,用户也没有时间静下心来去消化、吸收如此多的文档,需求毕竟不是小说,能够那么吸引读者。
(3)用户IT主管与开发人员,包括设计人员、编码人员、同行的专家
大多数分析人员可能最擅长的就是些写这类的文档了,往往也是那这类的文档给所有的读者看,其问题我们上边都说了,这里我们就不赘述了。
需要注意的是在描述需求时候传统的做法是以功能为主线,来展开描述,实际上如果是以数据为主线来描述需求也是一种很好的办法,在我们上面谈到的五元组中,从数据的角度来分析系统可以更容易实现向OOA、OOD的切换。
(4) 项目管理人员:包括项目经理、质量保证人员、测试人员、需求管理员、配置管理员、计划人员等等
把拿给开发人员看的需求文档给管理人员看,这也是分析人员常犯的毛病。管理人员实际上最关心的是需求列表。
在此基础上项目经理、质量保证人员可以据此来进入项目策划过程,测试人员可据此进入测试策划过程,需求管理员、配置管理员可以识别配置项制定相关的活动计划。没有这张表管理人员就很难高效地开展他们的管理活动,也就谈不到最基本的需求复用了。在上述的表中,需求的优先级是很重要的一列,对项目经理进行项目管理的平衡决策是很重要的,实际上需求的优先级可能比需求本身更重要。
3 需求描述的表示技巧
上面我们谈到了,需求文档是人与人之间交互的文档,是不同类型的人之间交互的文档,因此需求文档的可读性是一个很重要的方面,为了提高文档的可读性可以借鉴下面的一些做法:
在文档的描述中,适当运用链接,增强文档的可读性;
多用穷举的方式,以便于发现遗漏的需求;
通过适当的换行来提高可读性 ;
采用黑体、斜体、下划线、颜色等多种方式来突出重要内容;
定义标准的术语,以减少二义性,减少文档的页数;
在功能需求的描述中,对于类似的、统一的功能可以单独地进行详细描述,其他地方进行引用,或做为术语进行定义,以简化文档,减少重复。如;
· 录入功能
· 打印功能
· 条件查询功能
· 排序功能等等
结 语
尽管你按照上述的方法去做了,也不要期望能够编写出一份能体现需求应具备的所有特性的文档,无论你如何去细化、分析、评论和优化需求,都不可能达到完美,但是你能够做到“可接受”,写一份客户、用户、开发人员、管理人员都认可的一份需求,而不是完美的需求
大家做
Android开发,看到别人应用里一些好的功能,是不是很想得到源码,借鉴一下?既然Android是用JAVA开发的,那么我们就能很容易的通过反编译的到应用的源代码。下面我简单介绍下应该怎么操作。
具体步骤:
1.首先将apk文件后缀改为RAR并解压。
2.解压rar文件,得到其中的classes.dex文件
3.我们需要用到dex2jar 把dex文件转为jar文件。
3.1把解压得到的classes.dex文件放入dex2jar.bat 所在目录 )
3.2打开DOS命令行,进入dex2jar所在目录,运行 dex2jar.bat classes.dex 生成 classes_dex2jar.jar
1.对于注解类的支持
2.测试参数化
对于TestNG和JUnit都有不同的参数化设定功能,可是表现形式不同。参数化设定意味着当运行
测试的时候,参数可以从外部传入到这个测试单元中。
对于TestNG来说,要2部分,一是当构造测试代码时候,要用@Parameters(value=XXX)来表示这个测试需要什么参数,这样的优点是可以让测试更加灵活并且可以根据不同的参数来得到不同的期望结果,比如:
public class TestNGTest6_1_0 { @Test @Parameters(value="number") public void parameterIntTest(int number) { System.out.println("Parameterized Number is : " + number); } } |
然后,在我们的XML文件中,我们把参数的具体值设置进来,如下:
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="My test suite"> <test name="testing"> <parameter name="number" value="2"/> <classes> <class name="com.fsecure.demo.testng.TestNGTest6_0" /> </classes> </test> </suite> |
那么,如果我们要给的参数比较复杂,我们无法用一个简单的String或者 原子类型在XML文件”推“给测试方法,那么怎么办呢?这时候,我们就要用”拉“模型了,我们用一个DataProvider属性来给被测试的方法上声明它需要的数据,然后平行的,用@DataProvider注解来标注一个方法,让这个方法的返回值返回具体的数据,这样我们2个拼接下就完美了。
比如说:下面代码声明我们的测试类需要一个DataProvider:
@Test(dataProvider = "Data-Provider-Function") public void parameterIntTest(TestNGTest6_3_0 clzz) { System.out.println("Parameterized Number is : " + clzz.getMsg()); System.out.println("Parameterized Number is : " + clzz.getNumber()); } |
然后相应的我们用@DataProvider来标注一个方法,这个方法提供了数据:
//This function will provide the patameter data @DataProvider(name = "Data-Provider-Function") public Object[][] parameterIntTestProvider() { TestNGTest6_3_0 obj = new TestNGTest6_3_0(); obj.setMsg("Hello"); obj.setNumber(123); return new Object[][]{ {obj} }; } |
3.测试依赖性:
对于JUnit 来说,所有的测试彼此之间都是独立的,毫无依赖性。
但是对于 TestNG来说,我们完全可以让测试彼此之间有依赖性,做法就是dependsOnMethods属性来标识一个被依赖的测试方法:
@Test public void method1() { System.out.println("This is method 1"); } @Test(dependsOnMethods={"method1"}) public void method2() { System.out.println("This is method 2"); } |
谈起项目质量,那是一个让很多人都头疼的问题。明明项目规划的很好,可是等到交付的时候,质量却是一团糟。其实,很多人把质量和等级混淆了,质量不同于等级。质量是“一系列内在特性满足要求的程度”,而等级是“对用途相同但技术特性不同的产品或服务的级别分类”。
一个软件产品可以是高质量低等级的,也可能是低质量高等级的。低质量一定是问题,而低等级不一定是问题。所以,项目经理应与
项目管理团队负责权衡,以便同时达到要求的质量与等级水平。既然项目质量是一个大问题,接下来我们就一起看看应如何管理项目质量。
项目
质量管理包括执行组织确定质量政策、目标与职责的各过程和活动,从而使项目满足其预定的需求。它通过适当的政策和程序,采用持续的过程改进活动来实施质量管理体系。项目质量管理包含三大过程,分别是规划质量、实施质量保证和实施质量控制。
1.规划质量
规划质量是识别项目及其产品的质量要求和或标准,并书面描述项目将如何达到这些要求和或标准的过程。规划质量应与其他项目规划过程并行开展。例如,为满足既定的质量标准而对产品提出的变更建议,可能引发成本调整或进度调整或范围调整。
规划质量需参考的资料有范围基准,包括范围说明书、WBS、WBS词典等。此外,还包括干系人登记册、成本绩效基准、进度基准、风险登记册、事业环境因素和组织过程资产。
规划质量使用的工具与技术有很多,包括成本效益分析、质量成本、控制图、标杆对照、实验设计、统计抽样、流程图、质量管理方法、其它质量规划工具等。读者仅需了解这些内容即可,并根据自己的需要选择合适的工具。
规划质量的输出有质量管理计划、质量测量指标、质量核对表和过程改进计划。
2.实施质量保证
实施质量保证是审计质量要求和质量控制测量结果,确保采用合理的质量标准和操作性定义的过程。实施质量保证是一个执行过程,它使用实施质量控制过程所产生的数据,为持续过程改进创造条件。
实施质量保证的依据是项目管理计划、质量测量指标、
工作绩效信息和质量控制测量结果。
实施质量保证的工具与技术有很多,除了质量审计和过程分析之外,它包括规划质量和实施执行控制所使用的全部工具与技术。
实施质量保证的输出内容是组织过程资产(更新)、变更请求、项目管理计划(更新)、和项目文件(更新)。
3.实施质量控制
实施质量控制是检测并记录执行质量活动的结果,从而评估绩效并建议必要变更的过程。值得注意的是,质量控制的工作贯穿项目的始终。
实施质量控制的依据有项目管理计划、质量测量指标、质量核对表、工作绩效测量结果、批准的变更请求、可交付成果和组织过程资产。
实施质量控制的工具与技术有因果图、控制图、流程图、直方图、帕累托图、趋势图、散点图、统计抽样、检查和审查已批准的变更请求。其中,前七项被称作“石川七大基本质量工具”。
实施质量控制的输出有质量控制测量结果、确认的变更、确认的可交付成果、组织过程资产(更新)、变更请求、项目管理计划(更新)和项目文件(更新)。
质量是规划、设计和建造出来的,而不是检查出来的。预防错误的成本通常要比检查中发现并纠正错误的成本少得多。所以,我们在项目开发中应以预防胜于检查的态度去对待项目。有时候错误在所难免,当错误发生时,我们应持续改进,提高项目的质量,最终达到客户满意。
对于做
web端
自动化测试的人来说,可能接触selenium比
QTP还要多,但是我们在做基于selenium的二次开发的时候,经常会说到二次开发是 为了易于维护,很多人可能不懂得维护的价值是什么,和到底要维护什么。今天专门写一篇关于二次开发的
文章,希望能够帮到有需要做二次开发的人。
二次开发也就是我们常说的封装selenium,或者做框架。但是一个框架要包含丰富的类和方法。要有一套完整的体系来帮助我们进行封装。可以说框架的设 计思想就是整个框架的灵魂,如果设计思想很正确也就意味着这个框架成功了一半,剩下的就是我们怎么样用程序实现这个思想,在开发的过程中我们也许会用到一 些设计模式和引用一些开源框架。这些只是一个开发人员或者程序设计者的基本素质。至于如果把selenium能够有效的封装和一些基本思想,我们来详细的 了解一下。
在这篇文章里面只针对selenium的webdriver来进行讨论,我们不再对rc做任何的解释和说明。我们都知道webdriver的使用过程中, 贯穿始终的就是一个driver, 并且这个driver代表了一个浏览器的当前窗口,我们进行操作的过程中只是进行当前窗口的操作,也就是最这个current window进行的一系列的操作,如果我们需要对打开的新的window来进行操作的话,我们需要switchTo,包括操作frame,当然整个流程下 的操作确实让我们觉得不是很难编写,但是我们编写脚本的过程中需要用到的一些辅助功能可能就会很难的编写,比如最大化浏览器,视角
移动到操作的元素等等, 这个过程一次编写我们可以做到,但是反复的编写的话肯定是一个让人很头疼的过程,所以这个时候我们要去封装一些常用的方法,我们有了做一个比较完整的框架 的想法,但是我们忽然又意识到了,这样的话,我们需要把driver封装起来,因为整个测试的case都是针对的这个driver,并且只有一个 driver,这样子的话我们不允许创造多个的driver,也就意味着我们要把自己编写的小工具类和driver联系起来,并且我们的测试用例case 类也需要调用这个driver,其实很简单,我们可以用注入的方式来做,把driver当成tools类的一个属性值,然后注入到我们的case类中,也 可以通过set的方法来进行操作。有了这些基础,我们可以防止无限的编写重复的方法,这样我们有了自己的工具类。如果说这就是框架的话,就会显得非常的肤 浅,因为我们写的这些方法根本没有任何逻辑可言,只是把需要的方法统统的堆到了一起,所以这个时候我们需要想想用到某些方法来进行分一下层次。
Page类和Window类:
PageObject模式我们都知道,就是把资源都放入到page类里面,然后再编写逻辑类。这样的话就可以无限的复用这些资源,这只是笼统的讲了一下设 计的思想,至于PageObject到底怎么去实现这些设计呢?我们从webDriver的使用开始入手。webdriver是先从定义浏览器开始的。 WebDriver driver = new FirefoxDriver(); 这样我们就定义了一个firefox的浏览器,但是自动化的过程不可能只允许我们把定义浏览器的操作放在框架代码里面,那样的硬编码方式使我们的case 不存在可移植性了,如果进行兼容性测试的话,维护起来对这些case的修改量是比较大的,这种硬编码方式是我们不能够进行大量维护的,所以我们需要把定义 浏览器的过程完全放在case类里面,就是在我们写
测试用例的时候再去编写到底用什么浏览器,防止在编写框架的时候硬编码的形式把浏览器写死在了框架里 面。做到多浏览器的可维护性,对于我们进行兼容测试也有一定的帮助,这样的话我们需要对浏览器的选择部分要进行一定的编码设计,来完成浏览器的可选择性。 在我们定义完了浏览器之后,这个时候我们也许觉得就是开始查找元素了,但是在这个driver的基础上我们应该发现其实这个时候driver代表的整个页 面的操作。但是在页面的操作基础上我们应该意识到还有一个级别的操作,那就是window的操作,就是针对浏览器自身的操作。包括一些基本的返回,向前, 最大化,最小化,或者移动到制定元素的位置,调用js等等等,这些方法的级别是出于window级别的,和页面无关的。所以我们应该把这些所有的方法都封 装到单独的一个层次中,我们暂且称之为window包中,刚才的浏览器的选择的所有方法我们放browser包中。这样我们设计出了两个层次。下面的设计 该如何进行呢?我们知道pageObject的思想是把资源都放入到我们定义的page类里面,所以这个时候我们需要思考了,我们如何设计这里的page 类呢?按照pageObject的思想来看,page类应该是我们自己编写的,那样我们的框架是不是就可以放弃编写page类了呢?直接封装一些通用的方 法?显然是不对的,对于页面html源码操作的过程中,我们烦透了这些元素查找的硬编码方式,在一个case里面或者两个case里面反复的调用编写是让 人头疼的一件事情,所以我们可以把所有的单页面看做一个层次,里面和PageObject的思想一样,就是放入了通用的方法,但是它存在的意义不只是这样 简单,因为我们操作的过程中需要涉及到frame。并且页面和页面之间涉及到不同window之间的切换,所以如何协调window和page之间的关系 成为了我们需要注意的难点和重点,我们知道webdriver里面有一个方法叫做getWindowHandlers,这个方法可以获得所有的句柄,我们 想设计这个page类那么意味着我们需要去完美的配合window类,他们之间唯一的关联就是这个方法,所以我们可以设计一个概念,叫做页面集合,在创建 window对象的时候我们就会自动的出现一个页面集合器,它的功能就是管理所有的在操作过程中打开的页面。并且能够指定一个特殊的page,就是当前页 面。也就是webdriver中的driver对象,因为它一直都是针对当前页面编程的。那样我们的window类里面就存在了两个属性,一个收集器,一 个当前页。这样我们在window的级别上就能够完全操作page类了,这样我们在case类设计的时候,只需要通过window类的级别进行编码就可以 了,完全可以把page类当作一种资源来处理,我们所有需要的东西都是通过page来获取的。page类的设计中我们一定要编写通用的方法。比如获取 title等等等,最主要的一点就是要进行frame的处理操作,我们如何把frame完美的结合在page里面呢?我们在普通的webdriver脚本 编写过程中可能反反复复的switchTo的方法让我们很恼火,并且很难让我们理解这些脚本到底是想表达什么意思呢?所以我们需要进行对page进行层次 上的小分级,就是把page类再分为一个frame的类和一个模块的类,因为一个大型页面里面,通用的结构和模块是很多的。我们单独的定义一个模块类,可 以让这些相同的结构复用在里面的方法。定义frame类主要是处理把定位到frame的操作从case类中脱离出来,我们编写到page类里面或者 frame类里面,提供一种方式或者方法来进行frame的定位就可以了,简单实用,并且简化case类的编写,毕竟case类并不是由设计者来编写,尽 量做到最简化。当然我们可以成这整个层次都是page类,放在page包里面。
Element类:
Element类就是我们常用的driver.findElement()的那种形式,就是元素类,什么是元素类呢?元素就是我们需要定位的那些东西,我 们在操作过程中很难知道一个findElement到底查找的是什么,因为所有的标签形式都是通过id或者各种各样的定位方式来实现的。这个时候我们需要 把各种各样的findElement都统统的放在一起,造成的脚本难以理解让后续接手的脚本开发人员可能头疼不已,所以我们可以做一些简单的加工。当然我 们还要做的一个最大的
工作就是元素查找的封装。Element和Page类如何关联起来。我们知道page和webElement的关联就是一个 driver.findElement的方法。这样就可以在页面上查找元素了。所以我们也在element类中通过这种形式来进行他们之间的关联。我们通 过在element类中添加定位方式的形式来进行元素定位和page的关联,我们只需要在编写自己的page类的过程中直接加入element类就可以 了,element类提供了所有的关于element的方法,比如鼠标事件和键盘事件,还有更重要的元素定位方法。定位的方法在这里不做任何的推荐,因为 每个人的思路不同,实现的方式也不同,我个人比较偏向的做法是做一个xml来进行资源的管理,把所有需要的资源都放入到xml里面,这样我们就可以进行元 素的定位了。并且在后期维护中主要维护xml就可以进行对整个脚本进行维护了,不需要我们大量的重新进行源码的分析和修改了。当然这是设计的优化过程,因 为定位的实现我们还是需要自己来完成的,我们知道元素的定位方式各种各样,我们怎么来进行管理和定位呢?我们可以通过map的方法作为属性值来进行元素的 管理,他的各种定位方法存放在map中,我们需要的时候只需要调一下就可以了。通过观察源码findElement也是通过map的形式来进行元素定位存 储的。我们可以借鉴一下源码的实现方式。当然我们完全封装findElement也是可以的。说到这里可能我们有一个问题比较难以解决,那就是层级定位。 如果我们只是给element类添加定位方式的话,那么findElement提供的一级一级的定位方式我们就无法应用了,所以在element类中我们 必要还要提供findElement的方法进行层级定位。这只是为了把webdriver的所有方法都尽量应用到而已。其实通过xpath的方式我们就可 以基本上定位大多数的元素。剩下的内容就是我们对element内容的扩充了。我们可以把元素的更加具体的抽象出来,比如我们把 select,table,checkbox等等等的各种html标签元素显式的定义出来,在我们定义一个元素的时候我们能够更加清晰的看到这个元素的含 义,我们知道它是一个按钮或者table等等等,这些小元素的操作需要我们自己深入理解和开发,这里不做过多的介绍。
其他类:
通过这些层次的分析我们已经出现一个框架的雏形了,然后我们剩下的设计就是基于完善和优化了。在一个自动化过程中case类是非常重要的,我们需要知道 case类运行结束的结果报告和分析,所以case类的运行等等一系列的东西我们都得有统计,这些东西必须要我们提供一些类来实现,不过所幸的是,强大的 junit或者testng完全可以取代我们要去做的工作,他们可以很完美的提供这些功能,我们只需要介入这些开源包就可以了。我们使用QTP的过程中我 们经常会用到参数化,我们自动化的设计都有了,但是没有参数化的功能怎么办?我们应该先想象一下数据提供的方式。testng提供了一种参数化的形式,但 是它是需要在xml里面配置或者硬编码的形式来进行编写。不过它提供了一种dataprovider的方式来进行参数化,它传递参数的形式是 Object[][],我们可能希望使用参数的时候通过excel表格来完成,这些都是可以实现的,poi包提供了解析excel的功能,非常的强大。我 们可以自己编写解析类来进行参数化的功能编写,具体实现不再过多去说。调用的方式就简单多了,硬性的记住几个注解就可以了。case类的各种运行我们都有 了,还需要一些什么扩展呢?很显然就是日志的扩展。日志的设计也是很有技巧的。我们需要用日志监控某一些方法的话,如果前期没有直接加入日志功能,我们可 以通过spring的方式来进行日志切入操作。但是我们在观察日志的时候我们通常会希望知道到底运行到哪一步了,我们可以想一想,所有的操作都是基于 element或者window的,page的只是一个抽象出来的概念,所以我们只需要把日志加入到每一个element的方法和window的主要方法 里面就可以监控到整个运行的过程,毕竟我们不能够去亲自盯着屏幕一直。这样没个方法不外乎就是运行成功和失败,所以我们可以通过这种方式来进行编码。日志 的实现我们可以通过通过的log4j或者自己编写一个小的日志系统。都是可行的方案。
扩展类:
也许我们需要这些系统能够有良好的可移植性,我们可以自己编写类加载器,为以后做整个自动化的测试平台做准备。最主要都是我们做的这些操作可能需要越来简 单,所以我们可能会因为引入注解的方式来提供编码效率,所以我们还需要为注解类做一些辅助的工作。当然这些注解的开发需要我们做足足够的需求研究,并不是 无谓的去开发各种注解。这些注解的应用应该说很广泛,不再多说注解的好处。并且注解还有一点可应用的地方就是放在数据库的操作中,在自动化测试中,其实数 据库的测试也是一个大的难点。在这里我们只讨论前端自动化的设计,不过多的讨论别的东西。
前面讲到的这些类的存在形式其实就是在框架里面的一种层次,我们谈论的这些都是基于webdriver的,并且主要基于前端自动化测试的,当然自动化测试 不只包括这些,还包括服务器端,接口自动化,单元自动化等等。我个人的能力水平也是很有限,可能很多地方说到的不是很到位,希望能够通过这篇文章能够给那些希望学习自动化,希望编写小测试框架的童鞋,一点点的启发。
有几个学员经常会对线上与线下
测试结果不一样的问题产生纠结。。。。所以还是统一写一篇这样的
文章吧
其实这个问题本身不用纠结,就好比再牛逼的双胞胎还是有他们不一样的地方。本身
性能测试就是一个预估风险、排查瓶颈、了解系统现有性能的一个手段。就好比小时候你是个好孩子,但不意味这你长大了也是一个好孩子,也许你会像海波兄那样的。。。。。so,性能测试只是一种手段,减小风险的方法而已。
再者,本身线上和线下的测试结果就不太具有可比性,原因为:
1、线下与线上机器环境配置的差异
2、线下和线上业务数据的差异,虽然我们线下要最大可能的模拟用户行为,但你不能拿保证100%的模拟啊,那么多用户你都能兼顾到??????
3、线下和线上产生压力时间的差异,线下是模拟高压力大并发的情况,而线上通常压力不大,大并发主要集中在某几个特殊时段。
说道这里,又会有童鞋继续纠结了,那为毛还做测试啊,都不准确,做个毛毛??????好吧,那我想反问你一句,一辆汽车开的人不同,开车的习惯不同,会对车造成不同程度的影响,既然我们没法100%测试模拟,那我们干脆就产出汽车后直接卖给你好了,做个什么测试和路测,多tmd费劲。对吧?这时候你不干了,你说那多危险,万一有大问题呢,不就要了我的命了吗?呃。。。。这时候你明白了?那换到性能测试中就不明白了?
我们做性能测试的意义其实很简单:
1、预防、评估风险,如果有大问题可以早点发现,减小风险。这里理解极其简单,你程序存在内存泄漏的问题,难道线下2g和线上4g这个内存差异就不会有内存泄漏了????????????????这就好比,你不会骑永久牌自行车,难道给你换个小强牌(瞎编的。。。)自行车你就瞬间会骑了?
2、前端性能测试。可以通过前端性能测试保证页面性能,给用户带来较好的用户体验。
3、单接口性能调优。主要目的是优化接口性能,排查接口性能问题,及应用内存隐患。
比如,我们会准备几种业务场景,比如全走DB和全走缓存,分别得到这几种场景下,应用最佳处理能力情况下,在测试中排查是否存在性能提升的地方,及代码问题导致的内存泄露等。
4、容量评估。可以根据线上机器比例,线下模拟配比来估算。
下面的这个题你知道输出结果是什么吗?试试吧!相信对每一个学
java的同学都是有用的!说不定下次你去
面试就是这个题!当然你是技术大牛可以忽略!
1、 不合理的大表全表扫描
详见:点击打开链接
v$session_longops视图记录了超过6秒的所有
SQL语句
这其中绝大部是全表扫描的语句!
2、 语句共享性不好
常出没在OLTP,由于app没有合理使用绑定变量,导致大量重复的语句Parse,浪费大量的shared pool,使CPU利用率居高不下
3、 过量的排序操作
有个原则:能不排序就不排序
特别是multi-pass,与事务设计、缺乏索引、优化器的选择等均有关系
4、 大量递归SQL语句
由sys执行,以大量的空间管理sql语句为甚
常见于大数据处理
作为DBA,大数据处理前,主动进行存储空间的分配
5、 优化器和统计信息
代码有时候,在
测试环境能跑,到了生产环境就“萎”了
这是因为,生产环境没有及时采集统计信息,导致
Oracle优化器不了解最新的数据和应用情况,而错误地选择了非优化的执行路径
所以,我们需及时采集统计信息,保证基于CBO的优化器能欢快运行
6、 不合理的参数设置
系统参数一定要调,还要合理地调
主要是些内存参数、进程参数等
7、 存储部署不合理
由于存储部署不合理导致I/O效率低下
处理方案:ASM、RAID10等
主要是C/S结构比较常见,几乎绝迹于B/S了
9、 Redo Log 设计不合理
Redo log文件设计太小,频繁触发checkpoint事件,导致内存紧张和I/O繁忙
Redo log文件文件组太少,则可能使归档无法赶上redo entries产生的速度
在部署一个系统的时候,出现下列问题:
有的客户端电脑(操作系统Win7或Win8)连接异常缓慢,打开一个业务窗口需要1分钟-2分钟,而另外的(
操作系统也是Win7或Win8)连接却很快,一两秒就能打开。
在所有的这些客户端电脑上ping服务器的时候,延迟都在1ms以下,上外网也很快,把慢的电脑直接连到服务器的交换机上也特别慢,因此基本排除网络方面的原因。
用这些慢的电脑打开
Windows远程桌面连接Windows2003服务器操作,也是特别的慢。
因此怀疑是操作系统的原因导致的,后来在网上搜索,查到解决方案,如下:
运行命令行命令netsh int tcp set global autotuninglevel=disable(可以写成批处理文件进行调用)修改系统参数即可解决。
这种情况在Win7、Win8等系统中普遍存在,在本次部署的案例项目上,一半以上的电脑都有此问题,据当初安装系统的工程师回忆,慢的这些客户端电脑与正常的电脑确实是用不同的操作系统安装盘所安装。
注:global autotuninglevel参数设计的初衷是启动自动调优.看来,项目上打算使用新技术或者新系统,还需要事先在
测试环境多探索一番.
1.会话标识未更新:登录页面加入以下代码
request.getSession(true).invalidate();//清空session
Cookie cookie = request.getCookies()[0];//获取cookie
cookie.setMaxAge(0);//让cookie过期
request.getSession(true).invalidate();//清空session
Cookie cookie = request.getCookies()[0];//获取cookie
cookie.setMaxAge(0);//让cookie过期
不是很明白session的机制,高手路过可以指教一下。
2.跨站点请求伪造:
在出错的url加参数sessionid。
response.getWriter().write( "<script>parent.location.href='dbase/admin/loginJsp.action?sessionId="+sessionId+"'</script>");
response.getWriter().write( "<script>parent.location.href='dbase/admin/loginJsp.action?sessionId="+sessionId+"'</script>");
如果带参数报ssl错误,使用下面的post方式传值:
response.getWriter().write( "<script language=\"javascript\"> " + "document.write(\"<form action=dbase/admin/loginJsp.action method=post name=formx1 style='display:none'>\");" + "document.write(\"<input type=hidden name=name value='"+sessionId+"'\");" + "document.write(\"</form>\");" + "document.formx1.submit();" + "</script>" ); response.getWriter().write( "<script language=\"javascript\"> " + "document.write(\"<form action=dbase/admin/loginJsp.action method=post name=formx1 style='display:none'>\");" + "document.write(\"<input type=hidden name=name value='"+sessionId+"'\");" + "document.write(\"</form>\");" + "document.formx1.submit();" + "</script>" ); |
3.启用不安全HTTP方法
修改
web工程中或者服务器web.xml,增加安全配置信息,禁用不必要HTTP方法
<security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> <http-method>PUT</http-method> <http-method>DELETE</http-method> <http-method>HEAD</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> |
修改web工程中或者服务器web.xml,增加安全配置信息,禁用不必要HTTP方法
<security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> <http-method>PUT</http-method> <http-method>DELETE</http-method> <http-method>HEAD</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> |
4.已解密登录请求
配置SSL,具体见http://serisboy.iteye.com/admin/blogs/1320231
在web.xml加入如下配置。
<security-constraint> <web-resource-collection > <web-resource-name >SSL</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> <security-constraint> <web-resource-collection > <web-resource-name >SSL</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> |
5.高速缓存的ssl页面
页面
<meta http-equiv="Pragma" contect="no-cache">
页面
<meta http-equiv="Pragma" contect="no-cache">
response.setHeader("Pragma", "No-cache");
response.setHeader("Pragma", "No-cache");
6.目录列表
配置文件目标拒绝访问。
在conf/web.xml下:
<servlet> <servlet-name> default </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet> <servlet-name> default </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> |
把listings对应的value设置为fasle.
或者把上面的这个servlet加到你的虚拟路径下的web-inf/web.xml中,把servlet-name改为其它的,再加一下servlet-mapping
<servlet> <servlet-name> default1 </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name> default1 </servlet-name> <url-pattern> / </url-pattern> <servlet-mapping> <servlet> <servlet-name> default1 </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name> default1 </servlet-name> <url-pattern> / </url-pattern> <servlet-mapping> |