1、漏洞总数
AppScan Source:91
Fortify:121
2、Disclaimer.htm:34(Cross-Site Scripting:DOM)的漏洞Fortify能扫描出来,AppScan Source扫描不出来
另外,Fortify能扫描出比较多Persistent类型的XSS漏洞
并且归类比较好(分DOM、Persistent、Reflected类型列出)
3、AdminLoginServlet.java:35(Password Management:Hardcoded Password)的漏洞Fortify能扫描出来,AppScan Source扫描不出来
4、Fortify扫出的DBUtil.java:238(Access Control:Database)在AppScan中被归类到
SQL Injection
5、admin.jsp:18(Password Management:Empty Password)属于误报
<script language="javascript"> function confirmpass(myform) { if (myform.password1.value.length && (myform.password1.value==myform.password2.value)) { return true; } else { myform.password1.value=""; myform.password2.value=""; myform.password1.focus(); alert ("Passwords do not match"); return false; } } </script> |
6、Fortify会报比较多这类问题:
Code Correctness:Class Does Not Implement equals Hardcoded Domain in HTML Hidden Field J2EE Bad Practices J2EE Misconfiguration Missing Check against Null Password Management:Password in Comment Poor Error Handling System Information Leak:Incomplete Servlet Error Handling |
7、Fortify会报比较多transfer.jsp:32(Cross-Site Request Forgery)这类CSRF的问题,而AppScan Source没有扫出来
8、Fortify有扫出ServletUtil.java(Missing XML Validation)的问题,而AppScan Source没有扫出来
9、Fortify有扫出AdminServlet.java:65(Redundant Null Check)的问题,而AppScan Source没有扫出来
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。
单元测试目的?
执行单元测试,是为了证明某段代码的行为确实和开发者所期望的一致。
1 测试目的,一个是测试程序的整体逻辑,另一个是测试程序中一个独立的模块
2 通常的执行人员不一样,白盒一般是由专门的白盒测试人员完成,单元测试一般由程序员自己完
计划你的单元测试:
设计一系列的输入和预期结果
Eclipse中使用Junit:
eclipse中已经内置Junit,无需自己再安装
选择一个需要单元测试的工程,右键点击Properties
添加Junit依赖
选择Junit
每一个软件项目,不管是project类项目,还是产品类项目,都必须经历需求分析、系统设计、编码实现、集成測试、部署、交付、维护和支持的过程。在这个过程中,将生成各种各样不同的工件,包含文档、源程序、可执行代码、支持库。更可怕的是,频繁出现的变更是不可避免的,因此面向如此庞大且不断变动的信息集,怎样使其有序、高效地存放、查找和利用就成为了一个突出的问题。
针对这一问题,最早的开发者尝试过的解决的方法是通过手工来实现:
1)文档:每次改动时都另存为一个新的文件,然后通过文件名称进行区分,比如 "XXX 软件需求说明书V1.0, XXX软件需求说明书V1.1, XXX 软件需求说明书V2.0.",并且在文件里注明每次版本号变化的内容;
2) 源码:每次要改动时就将整个project文件夹复制一份,将原来的文件夹进行改名,比如 "XX 项目V1.0、 XX 项目1.01、 .",然后在新的文件夹中进行改动;
可是这样的方法,不仅十分繁琐,easy出错,并且会带来大量的垃圾数据。假设是团队协同开发或者是项目规模较大时,还是会造成非常大的混乱。非常显然,这样简陋的方法是无法应对这一问题的。
后来,有人尝试从制造工业领域引入了"
配置管理"这一概念,通过不懈的研究与实践,终于形成了一套管理办法和活动原则,这也就是软件配置管理。
通过软件配置管理,将对软件系统中的多重版本号实施系统的管理;全面记载系统开发的历史过程,包含为什么改动,谁作了改动,改动了什么;管理和追踪开发过程中危害软件质量以及影响开发周期的缺陷和变化。并对开发过程进行有效地管理和控制,完整、明白地记载开发过程中的历史变更,形成规范化的文档,不仅使日后的维护和升级得到保证,并且更重要的是,这还会保护宝贵的代码资源,积累软件財富,提高软件重用率,加快投资回报。
常见的配置管理工具
正如前面所述,因为软件配置管理过程十分繁杂,管理对象错综复杂,假设是採用人工的办法不仅费时费力,还easy出错,产生大量的废品。因此,引入一些自己主动化工具是十分有裨益的,这也是做好配置管理的必要条件。
正是因为如此,市场上出现了大量的自己主动化配置管理工具,这些工具的实现原理与基本机制均十分接近,但因为其定位不同,因此各有特点,下面我们就对一些常见的配置管理工具做一简单的介绍。
元老:CCC 、SCCS、 RCS
上个世纪七十年代初期加利福利亚大学的Leon Presser 教授撰写了一篇论文,提出控制变更和配置的概念,之后在1975年,他成立了一家名为 SoftTool的公司,开发了自己的配置管理工具:CCC,这也是最早的配置管理工具之中的一个。
在软件配置管理工具发展史上,继CCC之后,最具有里程碑式的是两个自由软件: Marc Rochkind 的SCCS (Source Code Control System) 和 Walter Tichy 的RCS (Revision Control System),它们对配置管理工具的发展做出了重大的贡献,直到如今绝大多数配置管理工具基本上都源于它们的设计思想和体系架构。
Rational 公司是全球最大的软件CASE 工具提供商,现已被
IBM 收购。或许是受到其拳头产品、可视化建模第一工具Rose 的影响,它开发的配置管理工具ClearCase 也是深受用户的喜爱,是如今应用面最广的企业级、跨平台的配置管理工具之中的一个。
ClearCase提供了比較全面的配置管理支持,当中包含版本号控制、
工作空间管理、Build管理等,并且开发者无需针对其改变现有的环境、工具和工作方式。
其最大的缺点就在于其价格不菲,每一个client用户许可证大约须要几千美金,所以在国内应用群体有限。
1) 版本号控制
ClearCase 不仅能够对文件、文件夹、链接进行版本号控制,同一时候还提供了先进的版本号分支和归本功能用于支持并行开发。另外,它还支持广泛的文件类型。
2)工作空间管理
能够为开发者提供私人存储区,同一时候能够实现成员之间的信息共享,从而为每一位开发者提供一致、灵活、可重用的工作空间域。
3) Build管理
对ClearCase 控制的数据,既能够使用定制脚本,也可使用本机提供的make 程序。
其最大的缺点就在于其价格不菲,每一个client用户许可证大约须要几千美金,所以在国内应用群体有限。
新秀:Hansky Firefly
做为Hansky公司
软件开发管理套件中重要一员的Firefly,能够轻松管理、维护整个企业的软件资产,包含程序代码和相关文档。 Firefly是一个功能完好、执行速度极快的软件配置管理系统,能够支持不同的
操作系统和多种集成开发环境,因此它能在整个企业中的不同团队,不同项目中得以应用。
Firefly基于真正的客户机/
server体系结构,不依赖于不论什么特殊的网络文件系统,能够平滑地执行在不同的LAN、WAN 环境中。它的安装配置过程简单易用,Firefly 能够自己主动、安全地保存代码的每一次变化内容,避免代码被无意中覆盖、改动。
项目管理人员使用 Firefly能够有效地组织开发力量进行并行开发和管理项目中各阶段点的各种资源,使得产品公布易于管理;并能够高速地回溯到任一历史版本号。系统管理员使用Firefly 的内置工具能够方便的进行存储库的备份和恢复,而不依赖于不论什么第三方工具。
开源奇葩: CVS
CVS 是Concurrent Versions System 的缩写,它是开放源码软件世界的一个伟大杰作,因为其简单易用、功能强大,跨平台,支持并发版本号控制,并且免费,它在全球中小型软件企业中得到了广泛使用。
其最大的遗憾就是缺少对应的技术支持,很多问题的解决须要自已寻找资料,甚至是读源码。
小工作组级:Merant PVCS
MERANT 公司的 PVCS 能够提供对软件配置管理的基本支持,通过使用其图形界面或相似SCCS 的命令,能够基本满足小型项目开发的配置管理需求。 PVCS 尽管功能上也基本能够满足需求,可是其性能表现一直较差,逐渐地被市场所冷落。
入门级:Microsoft Visual Source Safe
Visual Source Safe,即VSS ,是
微软公司为Visual Studio配套开发的一个小型的配置管理工具,准确来说,它仅能够称得上是一个小型的版本号控制软件。 VSS的优点在于其与Visual Studio实现了无缝集成,使用简单。提供了历史版本号记录、改动控制、文件比較、日志等基本功能。
但其缺点也是十分明显的,仅仅支持
Windows平台,不支持并行开发,通过 Check out - Modify - Check in的管理方式,一个时间仅仅同意一个人改动代码,并且速度慢、伸缩性差,不支持异地开发。甚至于微软本身也不採用其做为配置管理工具,而是使用一个名为SLM 的内部工具。
English » | | | | | | | | |
怎样选择配置管理工具
面对这些形形色色,各有千秋的配置管理工具,怎样依据组织特点、开发团队须要,选择切合适用的工具呢?笔者就结合工作实践中的经验与大家做一些交流与探讨。
配置管理工具的选择所需考虑的因素大体包含下面几个因素:
功能是否符合实际需求?是否符合团队特点?性能是否惬意?费用能否够接受?售后服务怎样?接下来,我们就这几方面逐一深入地探讨:
1)功能是否符合实际需求,是否符合团队特点
工具就是用来帮助您解决这个问题的,因此功能是否符合实际需求是最重要的推断因素。而大多数主流配置管理工具的基本功能都能够满足,因此主要须要推断下面几个因素:
并行开发支持
在团队协作开发过程中,有两种基本的模式:集体代码权和个体代码权。採用集体代码权模式进行开发时,一段代码可能同一时候会被多个开发者同一时候改动;而採用个体代码权模式进行开发时,每一段代码都始终被一个开发者独享,别人须要改动时也会通过该开发者完毕。
而配置管理软件针对这一情况,也採用了不同的策略:Copy-Modify-Merge(拷贝、改动、合并 ) 的并行开发模式、Check out-Modify-Check in(签出、改动、签入)的独占开发模式。在并行开发模式下,开发者能够并行开发、更改代码, Firefly会自己主动检測到代码冲突,并自己主动合并,或提示开发者手动解决。
表一、并行开发支持比較表
工具名称
说明
ClearCase
Copy-Modify-Merge 模式
Firefly
Copy-Modify-Merge 模式
CVS
Copy-Modify-Merge 模式
PVCS
Check out-Modify-Check in 模式
VSS
Check out-Modify-Check in 模式
异地开发支持
假设你的开发团队分布在不同的开发地点,就须要对工具的异地开发功能进行细致的评估了。大多数工具都提供基于 Web的界面,用户能够通过浏览器执行配置管理的相关操作,并且有些工具就通过这样的方法来实现对异地开发的支持。
这样的实现方法有太多的局限性,比如网络(Internet)连接带宽的限制、防火墙以及安全问题等。真正意义上的异地开发支持,是指在不同的开发地点建立各自的存储库,通过工具提供同步功能自己主动或手动同步。这样做的优点是与网络无关,即便各个开发地点之间没有实时连通的网络,也能够通过 E-Mail 附件等其他方式将同步包发给对方,实现手动的同步。
表二异地开发支持比較表
工具名称
说明
ClearCase
提供MultiSite 模块,通过自己主动或手动同步位于不同开发地点的存储库的方式,支持异地开发
Firefly
提供ServerSync 模块,通过自己主动或手动同步位于不同开发地点的存储库的方式,支持异地开发
CVS
无专门支持的模块
PVCS
无专门支持的模块
VSS
无专门支持的模块
值得说明的是,在不同开发点建立各自存储库的方式,主要适用于两个或两个以上位于不同地点的开发团队协作开发的情况。假设仅是採用虚拟团队合作的方式,开发者以个体的形式散落在不同地方,则更适合通过 Internet 直接操作远程的配置管理server。
跨平台开发支持
假设企业须要从事多个不同平台下的开发工作,就须要配置管理工具能够对跨平台开发提供支持,否则势必会给开发、測试、公布等各个环节带来不便,将使大量的时间被浪费于代码的手工上传、下载中。
表三跨平台开发支持比較表
工具名称
说明
ClearCase
支持常见的平台
Firefly
软件本身基于Java开发,可在 Windows、Linux、 Solaris、HP-UX、 AIX等常见平台上使用,平台之间的移植也非常方便
CVS
支持差点儿全部的操作系统
PVCS
软件本身基于Java 开发,能够支持常见的平台
VSS
仅支持Windows 操作系统
与开发工具的集成性
配置管理工具与开发工具是编码过程中最经常使用到两种工具,因此它们之间的集成性直接影响到开发者的便利性,假设无法良好集成,开发者将不可避免地在配置管理工具与开发工具之间来回切换。
表四与开发工具集成性比較表
工具名称
说明
ClearCase
直接与资源管理器集成,十分易用
Firefly
与常见开发工具无缝集成
CVS
对开发工具集成性较差
PVCS
仅支持Windows 操作系统
VSS
与Visual Studio开发工具包无缝连接,其他开发工具集成性差
2)性能是否惬意
配置管理工具软件的一些性能指标对于终于的选择也有着至关重要的影响。
执行性能
假设开发团队规模不大的情况下,配置管理工具软件的性能不会造成非常大影响,但假设项目规模比較大,团队成员逐渐增多的情况下,其执行性能就会带来非常大的影响。
表五执行性能比較表
工具名称
说明
ClearCase
server採用多进程机制,使用自带多版本号文件系统MVFS,对性能有较大负面影响。做为一款企业级、全面的开发配置管理工具,适用于大型开发团队
Firefly
server採用了多线程的应用server,性能表现优秀,做为一款企业级、全面的开发配置管理,能适用于50人到上千人的团队
CVS
较高的执行性能,适用于各种级别的开发团队
PVCS
server採用文件系统共享方式,对CPU、内存及网络要求较高,性能一般,仅适用于中小型项目团队,不适合于企业级应用
VSS
相对功能单一、简陋,适用于几个人的小型团队,在数据量不大的情况下,性能能够接受
易用性
表六易用性比較表
工具名称
说明
ClearCase
安装、配置、使用相对较复杂,须要进行团队培训
Firefly
在提供全面配置管理功能的情况下,安装、配置、使用较为简单,包含安装、配置、培训在内的整个实施周期一般不会超过一个月。
CVS
安装、配置较复杂,但使用比較简单,仅仅需对配置管理做简单培训就可以
PVCS
使用比較简单,仅仅需对配置管理做简单培训就可以
VSS
安装、配置、使用均较简单,非常easy上手使用
从用户界面、与开发工具的集成性角度来说,这几款主流的配置管理软件均有较好的设计,均有较好的易用性。
安全性
表七安全性比較表
工具名称
说明
ClearCase
採用C/S模式,须要共享server上的存储文件夹以供client訪问,这将带来一定安全隐患
Firefly
server上的存储文件夹不用共享,对client不透明,client不可直接訪问存储文件夹,使系统更安全可靠
CVS
採用C/S 模式,不须要共享server上的存储文件夹,安全性较好
PVCS
基于文件系统共享,并且须要以"可写 "的权限共享存储文件夹,存在较大的安全隐患
VSS
基于文件系统共享实现对server的訪问,须要共享存储文件夹,这将带来一定安全隐患
3)费用能否够接受
Rational ClearCase 、Hansky Firefly 两款均属于企业级配置管理工具软件 ,ClearCase价格较贵,,相比之下 Hansky Firefly 是一款不错的选择。
而 PVCS其价格大约是每client几百美元的水平,对于国内企业来说,性价比不太划算。 VSS 是微软打包在Visual Studio开发工具包之中的,显然花费的精力不大,价格也比較廉价,能够做为个人、小项目团队版本号控制之用。
而 CVS则是一款全然免费的开源软件,性能较之企业级配置管理工具差距不大,也是一种不错的选择。
4) 售后服务怎样
表八售后服务比較表
工具名称
说明
ClearCase
大型商用软件,已被IBM公司收购,但国内市场拓展有限,因此服务支持会受到限制。如今中国用户的支持是由位于澳大利亚悉尼的支持中心联系
Firefly
大型商用软件,已在中国成立分公司,全面拓展市场之中,在北京设有支持中心
CVS
做为开源软件,无官方支持,须要用户自己查找资料解决技术问题,如今也出现专门为CVS 做技术支持的公司
PVCS
在中国市场开拓有限,国内没有支持中心
VSS
做为微软的非核心产品,技术支持有限。在其站点上有提供一些常见问题,仅仅有对正式购买的用户提供一定的技术支持
售后服务与产品支持也是一个非常重要的考察点,工具在使用过程中出现这样那样的问题是非常寻常的事,有些是因为使用不当,有些则是工具本身的缺陷。这些问题都会直接影响到开发团队的使用,因此随时能够找到专业技术人员解决这些问题就变成十分重要。
实例说明
最后,笔者介绍几个实际的案例,希望对大家选择软件配置管理工具软件有帮助。
案例一
某公司拥有10 名专职开发者以及一些兼职的开发者,主要从事 Windows和Linux 平台下的软件开发,採用的工具包含Visual Studio 系列、 GCC 等。为了能够加强版本号控制与配置管理工作,决定引入一些自己主动化配置管理工具。
经过谨慎的选择,採用了两步走的方法:
1) 首先採用了Visual Studio 软件包中的 VSS做为配置管理工具;
<SPAN style="FONT-SIZE: 9pt;"word-break: break-all; line-height: 21.6000003814697px; margin: 10px 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: #ffffff;"> ...
一般来说,维护需求的项目时间都是比较紧急的,维护嘛,用户等着上线用的啊,其实在这里,我们对维护需求和新增需求的定义都不太清晰。
怎么理解呢?我个人的理解:
维护需求,是指用户提出在系统使用过程中的问题以及整合目前用户觉得需要改进系统的一些功能的需求文档。
新增需求,就是指用户在不满足当前系统使用功能的前提下提出一些不包含之前的需求文档内的新增的功能而形成的需求文档,它没有包含用户继续解决的用户bug。
因此,我个人从定义上区分,维护需求多数只是针对用户问题去修复并验证bug,当然也有掺入一些用户界面体验、新增的小功能点吧。而新增需求,在项目开发中可能就不是一个小新增功能点了,更多的是大的项目系统分支、功能模块,需求业务规则的编写程度需要更明细。
为什么要创建索引呢?这是由于,创建索引能够大大提高系统的性能。
第一,通过创建唯一性索引,能够保证
数据库表中每一行数据的唯一性。
第二,能够大大加快 数据的检索速度,这也是创建索引的最基本的原因。
第三,能够加速表和表之间的连接,特别是在实现数据的參考完整性方面特别有意义。
第四,在使用分组和排序 子句进行数据检索时,相同能够显著降低查询中分组和排序的时间。
第五,通过使用索引,能够在查询的过程中,使用优化隐藏器,提高系统的性能。
或许会有人要问:添加索引有如此多的长处,为什么不正确表中的每个列创建一个索引呢?这样的想法固然有其合理性,然而也有其片面性。尽管,索引有很多长处, 可是,为表中的每个列都添加索引,是很不明智的。这是由于,添加索引也有很多不利的一个方面。
第一,创建索引和维护索引要耗费时间,这样的时间随着数据 量的添加而添加。
第二,索引须要占物理空间,除了数据表占数据空间之外,每个索引还要占一定的物理空间,假设要建立聚簇索引,那么须要的空间就会更大。
第三,当对表中的数据进行添加、删除和改动的时候,索引也要动态的维护,这样就减少了数据的维护速度。
索引是建立在数据库表中的某些列的上面。因此,在创建索引的时候,应该细致考虑在哪些列上能够创建索引,在哪些列上不能创建索引。一般来说,应该在这些列 上创建索引,比如:
在常常须要搜索的列上,能够加快搜索的速度;
在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
在常常常使用在连接的列上,这 些列主要是一些外键,能够加快连接的速度;
在常常须要依据范围进行搜索的列上创建索引,由于索引已经排序,其指定的范围是连续的;
在常常须要排序的列上创 建索引,由于索引已经排序,这样查询能够利用索引的排序,加快排序查询时间;
在常常使用在WHERE子句中的列上面创建索引,加快条件的推断速度。
相同,对于有些列不应该创建索引。一般来说,不应该创建索引的的这些列具有下列特点:
第一,对于那些在查询中非常少使用或者參考的列不应该创建索引。这是因 为,既然这些列非常少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于添加了索引,反而减少了系统的维护速度和增大了空间需求。
第二,对于那 些仅仅有非常少数据值的列也不应该添加索引。这是由于,由于这些列的取值非常少,比如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的非常大比 例,即须要在表中搜索的数据行的比例非常大。添加索引,并不能明显加快检索速度。
第三,对于那些定义为text, image和bit数据类型的列不应该添加索引。这是由于,这些列的数据量要么相当大,要么取值非常少。
第四,当改动性能远远大于检索性能时,不应该创建索 引。这是由于,改动性能和检索性能是互相矛盾的。当添加索引时,会提高检索性能,可是会减少改动性能。当减少索引时,会提高改动性能,减少检索性能。因 此,当改动性能远远大于检索性能时,不应该创建索引。
创建索引的方法和索引的特征
创建索引的方法
创建索引有多种方法,这些方法包含直接创建索引的方法和间接创建索引的方法。直接创建索引,比如使用CREATE INDEX语句或者使用创建索引向导,间接创建索引,比如在表中定义主键约束或者唯一性键约束时,同一时候也创建了索引。尽管,这两种方法都能够创建索引,但 是,它们创建索引的详细内容是有差别的。
使用CREATE INDEX语句或者使用创建索引向导来创建索引,这是最主要的索引创建方式,而且这样的方法最具有柔性,能够定制创建出符合自己须要的索引。在使用这样的方式 创建索引时,能够使用很多选项,比如指定数据页的充满度、进行排序、整理统计信息等,这样能够优化索引。使用这样的方法,能够指定索引的类型、唯一性和复合 性,也就是说,既能够创建聚簇索引,也能够创建非聚簇索引,既能够在一个列上创建索引,也能够在两个或者两个以上的列上创建索引。
通过定义主键约束或者唯一性键约束,也能够间接创建索引。主键约束是一种保持数据完整性的逻辑,它限制表中的记录有相同的主键记录。在创建主键约束时,系 统自己主动创建了一个唯一性的聚簇索引。尽管,在逻辑上,主键约束是一种重要的结构,可是,在物理结构上,与主键约束相相应的结构是唯一性的聚簇索引。换句话 说,在物理实现上,不存在主键约束,而仅仅存在唯一性的聚簇索引。相同,在创建唯一性键约束时,也同一时候创建了索引,这样的索引则是唯一性的非聚簇索引。因此, 当使用约束创建索引时,索引的类型和特征基本上都已经确定了,由用户定制的余地比較小。
当在表上定义主键或者唯一性键约束时,假设表中已经有了使用CREATE INDEX语句创建的标准索引时,那么主键约束或者唯一性键约束创建的索引覆盖曾经创建的标准索引。也就是说,主键约束或者唯一性键约束创建的索引的优先 级高于使用CREATE INDEX语句创建的索引。
索引的特征
索引有两个特征,即唯一性索引和复合索引。
唯一性索引保证在索引列中的所有数据是唯一的,不会包括冗余数据。假设表中已经有一个主键约束或者唯一性键约束,那么当创建表或者改动表时,SQL Server自己主动创建一个唯一性索引。然而,假设必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。当创建唯一性索引 时,应该认真考虑这些规则:当在表中创建主键约束或者唯一性键约束时,SQL Server自己主动创建一个唯一性索引;假设表中已经包括有数据,那么当创建索引时,SQL Server检查表中已有数据的冗余性;每当使用插入语句插入数据或者使用改动语句改动数据时,SQL Server检查数据的冗余性:假设有冗余值,那么SQL Server取消该语句的运行,而且返回一个错误消息;确保表中的每一行数据都有一个唯一值,这样能够确保每个实体都能够唯一确认;仅仅能在能够保证实体 完整性的列上创建唯一性索引,比如,不能在人事表中的姓名列上创建唯一性索引,由于人们能够有同样的姓名。
复合索引就是一个索引创建在两个列或者多个列上。在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。当创建复合索引时,应该考虑 这些规则:最多能够把16个列合并成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;在复合索引中,所 有的列必须来自同一个表中,不能跨表建立复合列;在复合索引中,列的排列顺序是很重要的,因此要认真排列列的顺序,原则上,应该首先定义最唯一的列,例 如在(COL1,COL2)上的索引与在(COL2,COL1)上的索引是不同样的,由于两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语 句中的WHERE子句必须參考复合索引中第一个列;当表中有多个关键列时,复合索引是很实用的;使用复合索引能够提高查询性能,降低在一个表中所创建的 索引数量。
无论缺陷预防
工作贯彻落实地多好,软件组件总有缺陷。这很明显,因为开发商无法阻止/消除
软件开发周期的所有缺陷。因此,软件必须进行彻底的
测试,然后才交付给最终用户。测试人员的责任是:设计既可以(ⅰ)找软件缺陷,又能(ii )评估该软件的性能,可用性和可靠性等方面的测试。
现在,为了实现这些目标,测试人员必须(往往是从一个非常大的执行域中)选择和/或制定
测试用例的有限数量。不幸的是,完整的测试通常不是在这个范围,预算和时间的约束内实现和/或执行的。重要的是,当测试开始失控且不按计划地运行时,由于预期无法实际,测试人员往往承受了来自管理层和利益相关者的巨大压力。
因此,测试人员必须有效地计划测试并制定正确的测试用例,选择并执行合适的用例,监控过程,以确保有效利用工作资源和时间。所以,要列出这些无疑是一项艰巨的任务;要有效地实施,测试人员需要受过适当的教育和培训并拥有赢得管理层支持的能力。
一般情况下,测试人员会用两种不同的测试方法,其中,使用常规方法,测试人员主要是尝试用所有可能的输入去测试一个模块或组件,用所有可能的软件结构去实践。尽管这种做法仍在使用,测试人员却在慢慢灌输推理软件的一切的价值,最终使他们能够检测出所有可能存在的缺陷。但见多识广且有学问的测试员们都明白,这在现实或经济上是不可行,不可实现的目标。
现在,另一种方法可能就是让测试员们随机选择测试输入,希望这些测试能将大的缺陷找出来。不过,测试专家认为,随机生成的测试输入在评估系统的质量属性方面表现纪录欠佳。所以,从测试的角度来看,这是一个无休止的争论和悬而未决的问题。尽管如此,我们还是认为,测试员的最终目标是了解测试的功能、输入/输出域和使用环境,等等。
同样,对于某些特定类型的测试,测试人员也需要详细地了解代码是如何构造的。此外,测试人员也需要利用关于常在软件开发或维护过程中生成的特定缺陷的知识。有了这些信息,测试者就必须明智地选择测试输入的子集,以及被认为最有可能找测试过程中条件和限制内的缺陷的测试输入组合。然而,这个过程需要时间和精力。所以,测试人员知道且赞同:只有开发出基于执行的测试的有效测试用例,才能最大化和/或优化对时间和资源的利用。
“有效测试用例”我们是指:“一个很可能找出缺陷的测试用例” 。因此,制定有效测试用例的能力对于一个组织迈向一个更高质量的测试过程来说是非常重要的;反过来,一个组织迈向一个更高质量的测试过程对制定有效测试用例的能力也有许多积极影响。
例如,如果测试用例是有效的,那么:
检测缺陷的概率更大。
更有效地利用组织资源。
测试再用的可能性更高。
更符合测试、项目进度、预算,更重要地,提供更高质量的软件产品的可能性。
测试用例设计方法 - 概念化
上面介绍了有效测试用例的种种好处,但缜密考虑测试人员用来设计这些有效测试用例的方法也同样重要。为了回答这个问题,有必要把软件作为一个精心设计的产品来查看和/或检查。现在,有了这个观念,就有两个基本方法可以用来设计测试用例:
黑盒(有时也称为功能或规格)测试方法。
白盒(有时也称为clear或透明盒 )测试方法。
使用
黑盒测试方法,测试人员把SUT (测试中的软件)当作一个不知道其内部结构(即如何运作)的不透明盒子,测试人员只知道它的作用。使用这种方法的SUT的大小可以是一个简单的模块、成员函数、对象群、一个子系统、或一个完整的软件系统。此外, SUT的基础行为或功能的描述可以由正式规格,输入/处理/输出图( IPO),或一套定义明确的先决、后置条件来提供;重要的是,另一个值得一提的信息来源是:需求规格说明文档,通常描述SUT的功能,输入及预期输出。现在,鉴于上述来源,测试员提供指定输入到SUT,进行测试运行,然后确定所产生的输出是否与说明文档中提供的一致。因为黑盒测试方法只考虑了软件的行为和功能,它通常被称为
功能测试,或基于规范的测试。这种方法特别有用,极有助于找到要求和规格中的缺陷。
与此相反,
白盒测试方法关注将被测试的软件的内部结构。因此,使用白盒测试方法来设计测试用例,测试人员应该先了解结构,且为了实现这一目标,必须可随时参考和理解代码或适当的类伪代码的要求。一旦对结构有了必要的了解,测试者就可以选择合适的测试用例去实践特定的内部结构要素,并确定它们是否正常工作。例如,测试用例通常被设计来实践所有语句或发生在一个模块或成员函数中的真/假分支。但是,由于白盒测试的设计,执行和结果分析非常耗时,这种方法被限制和/或通常只适用于软件的小部分,如模块或成员函数。然而,白盒测试方法对于找出设计和基于代码的控件的逻辑缺陷和顺序缺陷,初始化缺陷和数据流缺陷等特别有用。
然而,从测试员的角度来看,要实现向用户提供低缺陷高质量的软件的目标,必须把这两种方法都用来设计测试用例。另外,这两种方法都支持测试员选择有限数量的将被用于测试的测试用例。这两种方法可以相互补充,因为每个都或许有助于找到某些特定类型的缺陷。重要的是,有了使用这两种方法设计出的一组测试用例,测试员找到SUT中各种不同类型缺陷的机会就增加了。
测试员还有一套有效的可再用的用来进行回归测试(更改后的重新测试),以及
软件测试的新版本。
上面是一份概要:使用任一设计方法制定测试用例的各种可用方法。
但是,在使用任一设计方法准备测试用例前有一些因素需要考虑清楚。它们分别是:
测试相关风险。
预期缺陷类型。
测试员的知识和经验。
测试水平和必须进行分组和管理的小组活动。
用于执行测试用例的工具。
应用程序,软件和问题域的类型。
客户要求,等等。
本人现在外包到阿里
工作,最近被分配的一个工作就是--路由器app测试,写这个
文章是为了记录一下,方便自己查看。
刚分到阿里,各种不适应。1.路由器板子性能超级差,还没开始测就挂了。2.分到手的电脑超级烂,打开一个页面都要卡上好久。再加到对那边环境的不熟悉,导致
心情非常烦躁。幸亏那边接口人帮我们换了好的电脑,开发们也一直加班加点,第二天来,基本功能能操作,说明基本能
测试了。
这个路由器有两个频段,2.4G和5G,可以通过
web页面和手机app控制路由器,而我主要测的是
苹果手机app的
软件测试。由于测试过程中会有bug,我们必须提交
手机端与路由器端的日志。手机端要抓日志,装了一个itool,itunes。通过这个工具可以实时抓捕手机日志。还可能通过airplay,映射图片到电脑,进行截图。至于路由器端的日志,可以装一个串口工具。而我因为板子串口有问题,只能装一个xshell,远程通过命令访问路由器。
测试总共经历了二周半,以下几点,用于提醒自己以后测试中注意:
1.在正式测试前,了解所有的功能需求,以防测试过程中出现误解。
2.如果有app与web端,两者可以交互测试。比如web端修改密码,手机端的反映。还有另外的一些,看会不会有影响。
3.同一个软件,再两个或两个以上的手机上操作会怎么样。
4.测一个功能A时,能不能连带测另一个功能B。再看看会不会影响A的功能。
5.软件重启或恢复出厂设置,会不会影响升级。
6.文本框名称密码,是否支持特殊字符,中文字符,全角字符。
7.了解一些基本的接口名称,可以通过日志知道,定位问题,知道是路由器的问题还是手机app的问题。
8.如果测了ios,就要再去测测android,因为ios比android稳定,可能你会发现更多android的问题。也有可能通过android测试发现一些android问题,交互测试也很重要。
以上是我现在能想到的一些,有新的内容,再补充。
在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为TASK_RUNNING。一旦一个运行中的进程时间片用完, Linux 内核的调度器会剥夺这个进程对CPU的控制权,并且从运行队列中选择一个合适的进程投入运行。
当然,一个进程也可以主动释放CPU的控制权。函数 schedule()是一个调度函数,它可以被一个进程主动调用,从而调度其它进程占用CPU。一旦这个主动放弃CPU的进程被重新调度占用 CPU,那么它将从上次停止执行的位置开始执行,也就是说它将从调用schedule()的下一行代码处开始执行。
有时候,进程需要等待直到某个特定的事件发生,例如设备初始化完成、I/O 操作完成或定时器到时等。在这种情况下,进程则必须从运行队列移出,加入到一个等待队列中,这个时候进程就进入了睡眠状态。
Linux 中的进程睡眠状态有两种:
一种是可中断的睡眠状态,其状态标志位TASK_INTERRUPTIBLE;
另一种是不可中断的睡眠状态,其状态标志位为TASK_UNINTERRUPTIBLE。可中断的睡眠状态的进程会睡眠直到某个条件变为真,比如说产生一个硬件中断、释放 进程正在等待的系统资源或是传递一个信号都可以是唤醒进程的条件。不可中断睡眠状态与可中断睡眠状态类似,但是它有一个例外,那就是把信号传递到这种睡眠 状态的进程不能改变它的状态,也就是说它不响应信号的唤醒。不可中断睡眠状态一般较少用到,但在一些特定情况下这种状态还是很有用的,比如说:进程必须等 待,不能被中断,直到某个特定的事件发生。
在现代的Linux操作系统中,进程一般都是用调用schedule()的方法进入睡眠状态的,下面的代码演示了如何让正在运行的进程进入睡眠状态。
sleeping_task = current;
set_current_state(TASK_INTERRUPTIBLE);
schedule();
func1();
/* Rest of the code ... */
在第一个语句中,程序存储了一份进程结构指针sleeping_task,current 是一个宏,它指向正在执行的进程结构。set_current_state()将该进程的状态从执行状态TASK_RUNNING 变成睡眠状态TASK_INTERRUPTIBLE。 如果schedule()是被一个状态为TASK_RUNNING 的进程调度,那么schedule()将调度另外一个进程占用CPU;如果schedule()是被一个状态为TASK_INTERRUPTIBLE 或TASK_UNINTERRUPTIBLE 的进程调度,那么还有一个附加的步骤将被执行:当前执行的进程在另外一个进程被调度之前会被从运行队列中移出,这将导致正在运行的那个进程进入睡眠,因为它已经不在运行队列中了。
我们可以使用下面的这个函数将刚才那个进入睡眠的进程唤醒。
wake_up_process(sleeping_task);
在调用了wake_up_process()以后,这个睡眠进程的状态会被设置为TASK_RUNNING,而且调度器会把它加入到运行队列中去。当然,这个进程只有在下次被调度器调度到的时候才能真正地投入运行。
2、无效唤醒
几乎在所有的情况下,进程都会在检查了某些条件之后,发现条件不满足才进入睡眠。可是有的时候进程却会在 判定条件为真后开始睡眠,如果这样的话进程就会无限期地休眠下去,这就是所谓的无效唤醒问题。在
操作系统中,当多个进程都企图对共享数据进行某种处理,而 最后的结果又取决于进程运行的顺序时,就会发生竞争条件,这是操作系统中一个典型的问题,无效唤醒恰恰就是由于竞争条件导致的。
设想有两个进程A 和B,A 进程正在处理一个链表,它需要检查这个链表是否为空,如果不空就对链表里面的数据进行一些操作,同时B进程也在往这个链表添加节点。当这个链表是空的时候,由于无数据可操作,这时A进程就进入睡眠,当B进程向链表里面添加了节点之后它就唤醒A 进程,其代码如下:
A进程:
1 spin_lock(&list_lock); 2 if(list_empty(&list_head)) { 3 spin_unlock(&list_lock); 4 set_current_state(TASK_INTERRUPTIBLE); 5 schedule(); 6 spin_lock(&list_lock); 7 } 8 9 /* Rest of the code ... */ 10 spin_unlock(&list_lock); |
B进程:
100 spin_lock(&list_lock);
101 list_add_tail(&list_head, new_node);
102 spin_unlock(&list_lock);
103 wake_up_process(processa_task);
这里会出现一个问题,假如当A进程执行到第3行后第4行前的时候,B进程被另外一个处理器调度投入运行。在这个时间片内,B进程执行完了它所有的指令,因此它试图唤醒A进程,而此时的A进程还没有进入睡眠,所以唤醒操作无效。在这之后,A 进程继续执行,它会错误地认为这个时候链表仍然是空的,于是将自己的状态设置为TASK_INTERRUPTIBLE然后调用schedule()进入睡眠。由于错过了B进程唤醒,它将会无限期的睡眠下去,这就是无效唤醒问题,因为即使链表中有数据需要处理,A 进程也还是睡眠了。
3、避免无效唤醒
如何避免无效唤醒问题呢?我们发现无效唤醒主要发生在检查条件之后和进程状态被设置为睡眠状态之前, 本来B进程的wake_up_process()提供了一次将A进程状态置为TASK_RUNNING 的机会,可惜这个时候A进程的状态仍然是TASK_RUNNING,所以wake_up_process()将A进程状态从睡眠状态转变为运行状态的努力 没有起到预期的作用。要解决这个问题,必须使用一种保障机制使得判断链表为空和设置进程状态为睡眠状态成为一个不可分割的步骤才行,也就是必须消除竞争条 件产生的根源,这样在这之后出现的wake_up_process ()就可以起到唤醒状态是睡眠状态的进程的作用了。
找到了原因后,重新设计一下A进程的代码结构,就可以避免上面例子中的无效唤醒问题了。
A进程:
1 set_current_state(TASK_INTERRUPTIBLE); 2 spin_lock(&list_lock); 3 if(list_empty(&list_head)) { 4 spin_unlock(&list_lock); 5 schedule(); 6 spin_lock(&list_lock); 7 } 8 set_current_state(TASK_RUNNING); 9 10 /* Rest of the code ... */ 11 spin_unlock(&list_lock); |
可以看到,这段代码在测试条件之前就将当前执行进程状态转设置成TASK_INTERRUPTIBLE了,并且在链表不为空的情况下又将自己置为TASK_RUNNING状态。这样一来如果B进程在A进程进程检查了链表为空以后调用wake_up_process(),那么A进程的状态就会自动由原来TASK_INTERRUPTIBLE变成TASK_RUNNING,此后即使进程又调用了schedule(),由于它现在的状态是TASK_RUNNING,所以仍然不会被从运行队列中移出,因而不会错误的进入睡眠,当然也就避免了无效唤醒问题。
4、Linux内核的例子
在Linux操作系统中,内核的稳定性至关重要,为了避免在Linux操作系统内核中出现无效唤醒问题,Linux内核在需要进程睡眠的时候应该使用类似如下的操作:
/* ‘q’是我们希望睡眠的等待队列 */
DECLARE_WAITQUEUE(wait,current);
add_wait_queue(q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
/* 或TASK_INTERRUPTIBLE */
while(!condition) /* ‘condition’ 是等待的条件*/
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(q, &wait);
上面的操作,使得进程通过下面的一系列步骤安全地将自己加入到一个等待队列中进行睡眠:首先调用DECLARE_WAITQUEUE ()创建一个等待队列的项,然后调用add_wait_queue()把自己加入到等待队列中,并且将进程的状态设置为 TASK_INTERRUPTIBLE 或者TASK_INTERRUPTIBLE。然后循环检查条件是否为真:如果是的话就没有必要睡眠,如果条件不为真,就调用schedule()。当进程 检查的条件满足后,进程又将自己设置为TASK_RUNNING 并调用remove_wait_queue()将自己移出等待队列。
从上面可以看到,Linux的内核代码维护者也是在进程检查条件之前就设置进程的状态为睡眠状态,
然后才循环检查条件。如果在进程开始睡眠之前条件就已经达成了,那么循环会退出并用set_current_state()将自己的状态设置为就绪,这样同样保证了进程不会存在错误的进入睡眠的倾向,当然也就不会导致出现无效唤醒问题。
下面让我们用linux 内核中的实例来看看Linux 内核是如何避免无效睡眠的,这段代码出自Linux2.6的内核(linux-2.6.11/kernel/sched.c: 4254):
4253 /* Wait for kthread_stop */
4254 set_current_state(TASK_INTERRUPTIBLE);
4255 while (!kthread_should_stop()) {
4256 schedule();
4257 set_current_state(TASK_INTERRUPTIBLE);
4258 }
4259 __set_current_state(TASK_RUNNING);
4260 return 0;
上面的这些代码属于迁移服务线程migration_thread,这个线程不断地检查kthread_should_stop(),直到kthread_should_stop()返回1它才可以退出循环,也就是说只要kthread_should_stop()返回0该进程就会一直睡 眠。从代码中我们可以看出,检查kthread_should_stop()确实是在进程的状态被置为TASK_INTERRUPTIBLE后才开始执行的。因此,如果在条件检查之后但是在schedule()之前有其他进程试图唤醒它,那么该进程的唤醒操作不会失效。
小结
通过上面的讨论,可以发现在Linux 中避免进程的无效唤醒的关键是在进程检查条件之前就将进程的状态置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE,并且如果检查的条件满足的话就应该将其状态重新设置为TASK_RUNNING。这样无论进程等待的条件是否满足, 进程都不会因为被移出就绪队列而错误地进入睡眠状态,从而避免了无效唤醒问题。
因为我这里有两台机子,我打算这样演示:
一台机子启动一个作为主点节的hub 和 一个作为次节点的hub(系统windows 浏览器为ie) ip为:192.168.40.24
另一台机子启动两个次节点hub,一个次节点浏览器为firefox,另一个浏览器为chrome (系统为windows),ip为:192.168.40.67
启动各节点hub
启动主节点hub命令:
java -jar selenium-server-standalone-2.24.1.jar -role hub
启动与主节点同一机子的次节点hub命令:
java -jar selenium-server-standalone-2.24.1.jar -role node -hub http://localhost:4444/grid/register -browser browserName="internet explorer"
启动另外一台机子的两个节点hub命令分别为:
启动firefox的
java -jar selenium-server-standalone-2.24.1.jar -Dwebdriver.firefox.bin="D:/Program Files/Mozilla Firefox/firefox.exe" -role webdriver -hub http://192.168.40.24:4444/grid/register -browser browserName=firefox
其中-Dwebdriver.firefox.bin="D:/Program Files/Mozilla Firefox/firefox.exe"这个是传我的firefox浏览器安装地址,不然找不到。
启动chrome的
java -jar selenium-server-standalone-2.24.1.jar -Dwebdriver.chrome.driver="D:/chromedriver.exe" -role webdriver -hub http://192.168.40.24:4444/grid/register -port 6666 -browser browserName=chrome
其中-Dwebdriver.chrome.driver="D:/chromedriver.exe",这个是传入我chromedriver.exe的放置地址。端口设为6666,防止和上面firefox的重复。
链接好后,可以在http://localhost:4444/grid/console可以查看到三个子节点的状态。启动详细方法可以参考前面的
文章。
Selenium Grid只是提供多系统、多浏览器的执行环境,而不是说任务一个test case丢给它就能并行运行。并行的运行我这里就交给testng了。看下面代码和配制。
testcase 代码 和 testng.xml
testcase代码:
package driver; import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class GridParallelTests { private WebDriver dr; DesiredCapabilities test; String baseUrl; @Parameters({"browser","nodeUrl","webSite"}) @BeforeMethod public void setUp(String browser,String nodeUrl,String webSite){ baseUrl = webSite; if(browser.equals("ie")) test = DesiredCapabilities.internetExplorer(); else if(browser.equals("ff")) test = DesiredCapabilities.firefox(); else if(browser.equals("chrome")) test = DesiredCapabilities.chrome(); else System.out.println("browser参数有误,只能为ie、 ff、chrome"); String url = nodeUrl + "/wd/hub"; URL urlInstance = null; try { urlInstance = new URL(url); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("实例化url出错,检查一下url格式是否正确,格式为:http://192.168.40.67:5555"); } dr = new RemoteWebDriver(urlInstance,test); dr.get(webSite); } @Test public void test(){ dr.get(baseUrl); dr.findElement(By.id("kw")).sendKeys("selenium"); dr.findElement(By.id("su")).click(); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("title:"+dr.getTitle()); } @AfterMethod public void quit(){ dr.close(); } } |
testng.xml:
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="Suite1" parallel="tests" thread-count="5"> <test name="test1"> <parameter name="browser" value="ff"/> <parameter name="nodeUrl" value="http://192.168.40.67:5555"/> <parameter name="webSite" value="http://www.baidu.com"/> <classes> <class name="driver.GridParallelTests"></class> </classes> </test> <test name="test2"> <parameter name="browser" value="chrome"/> <parameter name="nodeUrl" value="http://192.168.40.67:6666"/> <parameter name="webSite" value="http://www.baidu.com"/> <classes> <class name="driver.GridParallelTests"></class> </classes> </test> <test name="test3"> <parameter name="browser" value="ie"/> <parameter name="nodeUrl" value="http://192.168.40.24:5555"/> <parameter name="webSite" value="http://www.baidu.com"/> <classes> <class name="driver.GridParallelTests"></class> </classes> </test> </suite> |
运行testng.xml就可以了。
如果你是用ant把上面的参数传进去也是可以的。我这里就直接简单地放在testng.xml了。
1.启动Samples-Web-Start
Web Server时,提示Could not open port 1080-Port is already used by another server(wsaeaddrinuse)
解决方法:
找到xitami.cgf,将protbase=1000改为2000
[C:\Program Files\HP\LoadRunner\WebTours]
2.启动Samples-Web-Start Web Server时,提示your request was unsuccessful,Cannot create CGI process
解决方法:
a.可能未安装strawberry-perl.
b.如正常安装,请打开WebTours文件夹下的run.bat,查看其SET PATH路径设置是否正确,尤其是perl安装路径,此BAT后默认perl安装在C盘,c:\strawberry\c\bin;c:\strawberry\perl\bin 如你安装strawberry并非此路路径,在此修改BAT保存是没用的,需重新安装perl到C盘,或COPY一份到C盘下。重启start web server.服务.
3.LoadRunner Sample WebTours 点击Flights出现空白解决方法
解决方法:找环境变量,发现PERL5LIB变量中的值与LOG文件的匹配,值为:D:\oracle\product\10.2.0\db_2\perl\5.8.3\lib\MSWin32-x86;D:\oracle\product\10.2.0\db_2\perl\5.8.3\lib;D:\oracle\product\10.2.0\db_2\perl\5.8.3\lib\MSWin32-x86;D:\oracle\product\10.2.0\db_2\perl\site\5.8.3;D:\oracle\product\10.2.0\db_2\perl\site\5.8.3\lib;D:\oracle\product\10.2.0\db_2\sysman\admin\scripts;
去除所有带5.8.3的值,修改后的值为D:\oracle\product\10.2.0\db_2\sysman\admin\scripts;
此时sample运行正常。但不知道对ORACLE有什么影响。
怕对oracle有影响,我在StartServer.bat里加上了对这个变量的设置
编辑StartServer.bat 在最上面加上SET PERL5LIB=C:\oracle\product\10.2.0\db_1\sysman\admin\scripts