功能测试涉及了软件在功能上正反两面的测试,而非功能测试就是所有其他方面的测试。非功能测试包括性能、负载、安全、可靠性和其他很多方面。非功能测试有时也被称作行为测试或质量测试。非功能测试的众多属性的一个普遍特征是一般不能直接测量。这些属性是被间接地测量,例如用失败率来衡量可靠性或圈复杂度,用设计审议指标来评估可测性。
国际标准化组织(ISO)在ISO 9216和ISO 25000:2005中定义了几个非功能属性。这些属性包括:
可靠性
软件使用者期望软件能够无误运行。可靠性是度量软件如何在主流情形和非预期情形下维持它的功能,有时也包括软件出错时的自恢复能力。例如,自动定时保存现行文件的功能就可以归类到可靠性。
可用性
如果用户不明白应该如何使用,那么,即使是零差错的软件也会变得毫无用处。可用性测量的是用户
学习和控制软件以达到用户需求的容易程度。进行可用性研究、重视顾客反馈意见和对错误信息和交互内容的检查都能提高可用性。
可维护性
可维护性描述了修改软件而不引入新错误所需的
工作量。产品代码和测试代码都必须具备高度的可维护性。团队成员对代码的熟悉程度、产品的可测性和复杂度都对可维护性有影响。
可移植性
可移植性指一种计算机上的软件转置到其它计算机上的能力。软件移植是实现功能的等价联系,而不是等同联系。从狭义上讲,是指可移植软件应独立于计算机的硬件环境;从广义上讲,可移植软件还应独立于计算机的软件,即高级的标准化的软件,它的功能与机器系统结构无关,可跨越很多机器界限。
性能测试目的是验证软件系统是否能够达到用户提出的性能指标,同时发现软件系统中存在的性能瓶颈,优化软件,最后起到优化系统的目的。性能测试类型包括压力测试、负载测试,强度测试,容量测试等。因为各属性之间在范围上有重叠,很多非功能属性的名字是可以通用的。
压力测试
一般来说,压力测试的目的是要通过模拟比预期要大的工作负载来让只在峰值条件下才出现的缺陷曝光。内存泄漏、竞态条件、
数据库中的线程或数据行之间的死锁条件、和其他同步问题等等,都是压力测试能发掘出来的常见缺陷。 压力测试主要是为了测试硬件系统是否达到需求文档设计的性能目标,譬如在一定时期内,系统的cpu利用率,内存使用率,磁盘I/O吞吐率,网络吞吐量等。
负载测试
负载测试是要探讨在高峰或高于正常水平的负载下,系统或应用软件会发生什么情况。例如,一个网络服务的负载测试会试图模拟几千名用户同时连线使用该服务。测试的主要是软件系统的性能,譬如软件在一定时期内,最大支持多少并发用户数,软件请求出错率等。
平均无故障时间(MTBF)测试
MTBF测试是测量系统或应用软件在出错或当机前的平均运行时间。这一测试有几个变体,包括平均无错时间(MTTF)或平均无当机时间(MTTC)。技术含义略有不同,但实践上,这些词汇都是一个意思。
低资源测试
低资源测试是要确定当系统在重要资源(内存、硬盘空间或其他系统定义的资源)降低或完全没有的情况下会出现的状况。重要的是要预估将会发生什么,例如为文件存盘而无足够空间、或一个应用程序的内存分配失败时将会发生什么。
容量测试
与负载测试非常相似,容量测试一般是用来执行服务器或服务测试。目的是要确定系统最大承受量,譬如系统最大用户数,最大存储量,最多处理的数据流量等。容量模型通常建立在容量测试数据基础上。有了这些数据,营运团队(Operations)就能定计划什么时候增加系统容量:要么增加单机资源,如RAM、CPU和磁盘空间等;要么干脆增加计算机数目。
重复性测试
重复性测试是为了确定重复某一程序或场景的效果而采取的一项简单而“粗暴”(brute force)的技术。这个技术的精髓是循环运行测试直到达到一个具体界限或临界值,或者是不妙的境况。举个例子,一个操作也许会泄漏20字节的内存。这并不足以在软件的其他地方产生任何问题,但如果测试连续运行2000次,泄漏就可以增长到4万字节。如果是提供核心功能的程序有泄漏,那么这个重复性测试就抓到了只有长时间连续运行该软件才能发现的内存泄漏。通常有更好的办法来发现内存泄漏,但有时候,这种简单“粗暴”的方法也可以很有效。
兼容性测试
兼容性测试是指测试软件在特定的硬件平台上、不同的应用软件之间、不同的操纵系统平台上、不同的网络等环境中是否能够很友好的运行的测试。主要测试软件是否能在不同的操作系统平台上兼容,或测试软件是否能在同一操作平台的不同版本上兼容;软件本身能否向前或向后兼容;测试软件能否与其他相关的软件兼容;数据兼容性测试,主要是指数据能否共享等。
安全性测试
安全性测试是检查系统对非法侵入的防范能力。主要包括用户认证、系统网络安全和数据库安全方面的测试。安全测试期间,测试人员假扮非法入侵者,采用各种办法试图突破防线。例如:想方设法截取或破译口令;专门开发软件来破坏系统的保护机制;故意导致系统失败,企图趁恢复之机非法进入;试图通过浏览非保密数据,推导所需信息等。理论上讲,只要有足够的时间和资源,没有无法进入的系统。因此系统安全设计的准则是使非法侵入的代价超过被保护信息的价值,此时非法侵入者已无利图。
辅助功能测试
辅助功能测试保证软件公司开发的软件能被伤残人使用。其中任何应用程序都必须测试的特性包括:操作系统的设置测试、“内置”辅助特性的测试(包括Tab 键顺序、热键和快捷键)、编程访问的测试、以及辅助的技术工具的测试。辅助功能测试的一个重要方面就是使用辅助功能工具去测试应用程序, 这些工具包括,屏幕阅读器、放大镜、语音识别或者其他输入程序。
本地化测试
本地化就是将软件版本语言进行更改,本地化测试的对象是软件的本地化版本。本地化测试的目的是测试特定目标区域设置的软件本地化质量。本地化测试的环境是在本地化的操作系统上安装本地化的软件。从测试方法上可以分为基本功能测试,安装/卸载测试,当地区域的软硬件兼容性测试。测试的内容主要包括软件本地化后的界面布局和软件翻译的语言质量,包含软件、文档和联机帮助等部分。
配置测试
配置测试就是测试软件是否和系统的其他与之交互的元素之间兼容,如浏览器、操作系统、硬件等,验证被测软件在不同的软件和硬件配置中的运行情况。配置测试执行的环境是所支持软件运行的环境。测试环境适合与否严重影响测试结果的真实性和正确性。硬件环境指测试必须的服务器、客户端、网络连接设备、打印机等,软件环境指被测试软件运行时的操作系统、软件平台、数据库其他应用软件构成的环境。
可用性测试
可用性测试是在产品或产品原型阶段实施的通过观察或访谈或二者相结合的方法,发现产品或产品原型存在的可用性问题,为设计改进提供依据。可用性测试不是用来评估产品整体的用户体验,主要是发现潜在的误解或功能在使用时存在的错误。可用性测试适于解决的问题:确定测试产品的可用性水平;与预期目标、与竞争对手、与老版设计相比的可用性水平;比较不同方案,确定哪个方案更加可行。
一些在设计阶段能帮助发现潜在的性能问题的技巧:
提出疑问:
找出有潜在性能问题的地方。对网络交通的拥塞状况、内存管理的效率、数据库设计的合理性、或其他任何有关地方提出疑问。即使你并没有性能设计的解决方案,而只是通过让其他团队成员考虑性能问题,测试工程师也一样能够产生很大的影响力。
考虑全局:
不是片面地考虑局部的优化,而是考虑全面的用户场景。你将会在整个开发过程中有相对充足的时间深入性能场景的细节,但是在设计阶段的时间最好是花费在考虑从头到尾的场景上。
明确目标:
象“响应时间应该很快”这样的目标是不可度量的。应用SMART(Specific-具体的, Measurable-可度量的, Achievable-可实现的, Relevant-相关的, Time-bound – 有时限的)标准来设计目标。举个例子,“每个用户操作的执行时间必须不超过100毫秒,或上一版本的10%的时间之内将控制权返回应用程序”。
还有一个要考虑的技巧是预测哪里可能有性能问题,或者说分辨出哪些操作对用户来说是最重要的,从而是需要度量的。而往往最有效的办法就是在设计阶段就定义这些场景。
注:本文为《微软的软件测试之道》一书第12章内容以及网络收集后整理后的知识笔记,感谢本书作者Alan Page及网络内容相关作者。
摘要: 在上一次的selenium教程中我们介绍了如何使用maven自动配置selenium环境以及如何使用Maven来配置TestNG,那么这次的课程主要讲一下ReportNG的配置,在讲之前这块之前先讲讲为什么要讲reportNG,大家在使用TestNG时一定会发现其本身的报告生成不但简陋而且相当的不美观,而ReportNG正是一款为TestNG量身定做的报告生成插件,其报告美观、简约、清...
阅读全文
1、名词说明
1)主系统
制作
Linux系统并不是在一无所有的裸机上完成的,需要一个帮助我们制作系统的系统,这个系统就称为“主系统”。我们制作的系统就是依靠这个主系统来逐步完成的,因此主系统的选择非常重要。
2)目标系统
目标系统就是我们要完成的系统
3)临时系统
在制作目标系统 的过程中会有一个小型的过渡系统,这个系统在辅助制作完成目标系统后就不再使用了,所以称为临时系统。
4)编译工具
将Binutils(汇编工具)、GCC(编译器)合称为编译工具。
5)工具链
将Binutils(汇编工具)、GCC(编译器)和GLibc(标准C函数库)的组合称为工具链,有时候也会将一些需要用到的函数库作为工具链的一部分,使用工具链生成的可执行文件总是使用该工具链中的函数库。
在整个制作过程中,各个阶段都会产生工具链,为了能清楚并准确的表达某个工具链,对各个阶段的工具链名称做如下规定:
原工具链:主系统的工具链
预工具链:用于生成临时工具链的工具链
临时工具链:用于生成临时系统的工具链
目标工具链:用于生成目标系统的工具链
6)辅助命令
在编译软件包的过程中,除了工具链以外还需要一些命令的参与,如make,这些命令合称为辅助命令
7)工具链环境
将工具链连同辅助命令合称为工具链环境,不同的阶段会出现不同的组合,下面对各个阶段的工具链环境的称呼做如下规定:
预工具链环境:预工具链+主系统的辅助命令及基本函数库
临时工具链环境:临时工具链+临时系统中的辅助命令及基本函数库
目标工具链环境:目标工具链+目标系统中的辅助命令及基本函数库
8)运行环境
在一个运行的系统中可以存在多个不同的环境,这些环境中有各自的根目录及环境设置,这样的环境称为运行环境。在制作过程中各个阶段会处于不同的运行环境,这里对称呼做如下规定
主系统运行环境:-----
目标系统运行环境:目标系统所在目录为根目录的运行环境
9)纯净度
这里的纯净度并不是一个计量单位,而是用于表达某系统与其它系统的相关性,如果一个系统的运行依赖于另一个系统,那么这个系统是不纯净的。我们的目标是制作一个完全独立运行的系统。而且不管主系统是什么,只要目标系统制作出来了,那么目标系统就不会因为主系统的不同而有差异。
10)头文件
用于编译的一类文件,一般以.h作为文件的后缀,存放了函数的接口描述、结构体信息等程序设计相关的内容。
2、源代码的编译过程
1.数据库连接配置
<span style="font-family:'宋体', SimSun;font-size:16px;">//数据库连接配置<br>def db = [<br> url:'jdbc:h2:mem:groovy',<br> user:'root',<br> password:'root',<br> driver:'org.h2.Driver'<br>];<br></span>
2.创建数据库连接,这里使用到Groovy的Sql类。 <span style="font-family:'宋体', SimSun;font-size:16px;">//创建数据库连接<br>def sql = Sql.newInstance(db.url, db.user, db.password, db.driver);<br></span>
3.创建数据库表
<span style="font-size:14px;font-family:'宋体', SimSun;">//创建数据库表<br>sql.execute('''<br> CREATE TABLE account(<br> id integer NOT NULL,<br> name varchar(20),<br> url varchar(100)<br> )<br>''');<br> </span>
使用了groovy.sql.Sql类的execute方法执行一条SQL命令,在数据库groovy中创建了表account。
4.向数据库表中写入数据,并查询写入的数据
<span style="font-family:'宋体', SimSun;font-size:16px;">//写入数据<br>def datas=[<br> [100, 'Jack', 'http://www.jack.net'], <br> [101, 'Groovy', 'http://groovy.com'], <br> [102, 'Apache', 'http://apache.org']<br>];<br>datas.each { param-><br> sql.execute('INSERT INTO account(id, name, url) values(?,?,?)', param);<br>}<br>println('Insert After:');<br>sql.eachRow('SELECT id, name, url FROM account') { row-><br> printf('|%d|%s|%s|\n', row.id, row.name, row.url);<br>}<br></span>
从4中的程序可以看出,向表account中写入3条数据记录,然后查询并遍历出查询结果,再这一过程中使用了Groovy的闭包特性,列表数据结构。
下面是查询的结果:
<span style="font-family:'宋体', SimSun;font-size:16px;">Insert After:<br>|100|Jack|http://www.jack.net|<br>|101|Groovy|http://groovy.com|<br>|102|Apache|http://apache.org|<br></span>
5.查询数据
<span style="font-family:'宋体', SimSun;font-size:16px;">//查询第一行数据<br>def rs=sql.firstRow('SELECT * FROM account');<br>println('Query First Row:');<br>println(rs);<br></span>
Groovy的Sql类提供了大量的查询方法(具体参见Groovy的API),上面5中的代码是查询第一条记录,返回的类型是GroovyRowReasult,其实现了Map接口。打印输出则是一个Groovy的Map类型表示。如下:
<span style="font-family:'宋体', SimSun;font-size:16px;">Query First Row:<br>[ID:100, NAME:ZhangSan, URL:http://aiilive.blog.51cto.com]<br></span>
6.更新数据
<span style="font-family:'宋体', SimSun;font-size:16px;">def name='ZhangSan';<br>def url='http://aiilive.blog.51cto.com';<br>sql.executeUpdate("UPDATE account SET name=$name, url=$url where id=100");<br>println('Update After:');<br>sql.eachRow('SELECT id, name, url FROM account') { row-><br> printf('|%d|%s|%s|\n', row.id, row.name, row.url);<br>}<br></span>
7.删除数据
<span style="font-family:'宋体', SimSun;font-size:16px;">//删除指定条件的数据<br>name='Groovy';<br>sql.executeUpdate('DELETE FROM account WHERE name = ?', [name]);<br>name='Apache';<br>sql.execute('DELETE FROM account WHERE name=:name', ['name':name]);<br>println('Delete After:');<br>sql.eachRow('SELECT id, name, url FROM account') { row-><br> printf('|%d|%s|%s|\n', row.id, row.name, row.url);<br>}<br></span>
8.使用DataSet来处理数据
DataSet是Sql类的直接子类,用DataSet来操作数据库表更加有操作对象的样子。
<span style="font-family:'宋体', SimSun;font-size:16px;">def account=sql.dataSet('account');<br><br>account.add([id:103, name:'h2', url:'http://h2.org']);<br><br>name='51cto';<br>url='http://www.51cto.com';<br>account.add([id:104, name:name, url:url]);<br><br>println('DataSet Update After');<br>account.eachRow('SELECT id, name, url FROM account') { row-><br> printf('|%d|%s|%s|\n', row.id, row.name, row.url);<br>};<br>def accountRows=account.rows();<br>accountRows.each { row-><br> printf('|%d|%s|%s|\n', row.id, row.name, row.url);<br>}<br></span>
如上dataSet的参数account表示数据库中的表名。account是一个DataSet类型的对象,通过add方法想数据库表中添加一条记录,通过rows方法返回数据库表中的所有记录,如果rows方法添加参数则可以实现分页的功能。
上面通过groovy.sql包提供的API实现了数据库的基本操作,而该包中的类的其它更多的方法能够实现更丰富的操作。下面介绍数据库表和对象的映射操作以及集成Spring来操作数据库表。 数据库表和对象的映射操作:
1.准备工作
创建一个抽象的类SqlQuery
<span style="font-family:'宋体', SimSun;font-size:16px;">import groovy.sql.*;<br><br>abstract class SqlQuery {<br><br> def sql;<br> def query;<br><br> def SqlQuery(sql,query){<br> this.sql=sql;<br> this.query=query;<br> }<br><br> def execute(){<br> def rowList=sql.rows(query);<br> def results=[];<br> def size=rowList.size();<br> 0.upto(size-1) { index-><br> results <<this.mapRow(rowList[index]);<br> }<br> return results;<br> }<br><br> def abstract mapRow(row);<br>}<br></span>
创建一个Account类,其属性对应account表的字段
<span style="font-family:'宋体', SimSun;font-size:16px;">class Account {<br><br> def id;<br> def name;<br> def url;<br><br> @Override<br> public String toString() {<br> return "|$id|$name|$url|";<br> }<br>}<br></span>
创建一个AccountQuery类继承SqlQuery类并实现其中的抽象方法rowMap
<span style="font-family:'宋体', SimSun;font-size:16px;">import com.demo.db.SqlQuery;<br><br>class AccountQuery extends SqlQuery {<br><br> def AccountQuery(sql){<br> super(sql, 'SELECT id, name, url FROM account');<br> }<br><br> @Override<br> public Object mapRow(Object row) {<br> //映射非常之灵活<br> //def acc=new Account(id:row.getAt('id'),name:row.getAt('name'),url:row.getAt('url'));<br> //def acc=new Account(id:row.getAt(0),name:row.getAt(1),url:row.getAt(2));<br> def acc=new Account(<br> id:row.getProperty('id'),<br> name:row.getProperty('name'),<br> url:row.getProperty('url'));<br> return acc;<br> }<br>}<br></span>
AccountQuery类实现了rowMap方法,正是该方法将对象和表记录关联起来的,即达到了Table - Object的映射效果。
注意:上面代码中的注释部分是实现同样功能的不同写法。
2.通过SqlQuery类来查询account表的数据
<span style="font-family:'宋体', SimSun;font-size:16px;">//表映射对象查询<br>def accountQuery=new AccountQuery(sql);<br>def accList=accountQuery.execute();<br>println 'Table <-> Object Query: ';<br>accList.each { acc-><br> println acc.toString();<br>}<br></span>
accList则是Account对象的一个数组集合,这样就实现了数据库表和对象的映射操作。
集成Spring来操作数据库表:
1.准备工作
Spring提供了一个MappingSqlQuery类,我们可以用AccountQuery继承该类并实行其中的rowMap方法来达到数据库表和对象的映射。
<span style="font-family:'宋体', SimSun;font-size:16px;">import com.demo.db.Account;<br><br>import org.springframework.jdbc.object.MappingSqlQuery<br><br>class AccountQuery extends MappingSqlQuery {<br><br> def AccountQuery(ds){<br> super(ds,'SELECT id, name, url FROM account');<br> this.compile();<br> }<br><br> @Override<br> protected Object mapRow(ResultSet rs, int rowNumber) throws SQLException {<br> def acc=new Account(<br> id:rs.getInt('id'),<br> name:rs.getString('name'),<br> url:rs.getString('url'));<br> return acc;<br> }<br>}<br></span>
需要注意的地方是Spring的MappingSqlQuery类的带参数构造方法需要提供一个DataSource对象和查询的SQL命令。
创建一个Account类的DAO类,AccountDAO来实现数据库的操作,可以定义一个接口,然后另外实现该接口。应用程序操作数据库则只需要依赖定义的接口即可。这里省略了接口的定义。
<span style="font-family:'宋体', SimSun;font-size:16px;">class AccountDao {<br><br> def ds;<br><br> def getAccounts(){<br> def aq=new AccountQuery(ds);<br> return aq.execute();<br> }<br>}<br></span>
AccountDao有一个getAccounts方法,通过该方法则可以获取到表account的所有记录,通过AccountQuery的Mapping映射,将返回一个集合对象。
2.通过AccountDao类操作数据库
<span style="font-family:'宋体', SimSun;font-size:16px;">//集成Spring<br>def ds=new DriverManagerDataSource(db.url, db.user, db.password);<br>def accountDao=new AccountDao(ds:ds);<br>accLists=accountDao.getAccounts();<br>println 'Spring MappingSqlObject Query: ';<br>accList.each { acc-><br> println acc.toString();<br>}<br></span>
至此Groovy操作数据库就到这里了。Groovy让Java操作数据库变得轻巧许多,同时又没有引入多余复杂的API负担。了解PHP的数据库操作就能很快感受到Groovy让Java操作数据库不那么繁琐了。
题外话:
很遗憾过了这么就才反映过来,应该拥抱Groovy,在经历学习的Python,PHP和Nodejs之后,就更应该在已有的Java知识的基础上使用这个据说是"Java时代的王储"的动态语言。固步自封是可怕的,停留在舒适区更是后果不堪设想。放下,才能更轻松的上路,才能走的更远。
在面对动不动就SSH, SSM,TSH,JSF等框架堆砌Java应用的时候,会有那么一个夜晚,突然累了,疲惫了。人就像一只猴子被困在囚笼里,跳着千篇一律的舞蹈骗取欣赏,有自知索然无味却不得以为之的和这囚笼纠缠在一起,不得自己。不久,就被发展的潮流拍到沙滩,碎岩之上。
面对生产效率和机器效率之间的取舍不见得能达成一致的协议,但技术服务于生产,想法化为产品,则更需要容易表达和实现这些东西的技术,把复杂的事情简单化应该是技术追求的目标。
1、定义接口
使用interface来定义一个接口。接口定义同类的定义类似,也是分为接口的声明和接口体,其中接口体由常量定义和方法定义两部分组成。定义接口的基本格式如下:
[修饰符] interface 接口名 [extends 父接口名列表]{
[public] [static] [final] 常量;
[public] [abstract] 方法;
}
修饰符:可选,用于指定接口的访问权限,可选值为public。如果省略则使用默认的访问权限。
接口名:必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。一般情况下,要求首字母大写。
extends 父接口名列表:可选参数,用于指定要定义的接口继承于哪个父接口。当使用extends关键字时,父接口名为必选参数。
方法:接口中的方法只有定义而没有被实现。
例如,定义一个用于计算的接口,在该接口中定义了一个常量PI和两个方法,具体代码如下:
public interface CalInterface { final float PI=3.14159f;//定义用于表示圆周率的常量PI float getArea(float r);//定义一个用于计算面积的方法getArea() float getCircumference(float r);//定义一个用于计算周长的方法getCircumference() } |
注意:
与Java的类文件一样,接口文件的文件名必须与接口名相同。
实现接口
接口在定义后,就可以在类中实现该接口。在类中实现接口可以使用关键字implements,其基本格式如下:
[修饰符] class <类名> [extends 父类名] [implements 接口列表]{
}
修饰符:可选参数,用于指定类的访问权限,可选值为public、abstract和final。
类名:必选参数,用于指定类的名称,类名必须是合法的Java标识符。一般情况下,要求首字母大写。
extends 父类名:可选参数,用于指定要定义的类继承于哪个父类。当使用extends关键字时,父类名为必选参数。
implements 接口列表:可选参数,用于指定该类实现的是哪些接口。当使用implements关键字时,接口列表为必选参数。当接口列表中存在多个接口名时,各个接口名之间使用逗号分隔。
在类中实现接口时,方法的名字、返回值类型、参数的个数及类型必须与接口中的完全一致,并且必须实现接口中的所有方法。例如,编写一个名称为Cire的类,该类实现5.7.1节中定义的接口Calculate,具体代码如下:
public class Cire implements CalInterface { public float getArea(float r) { float area=PI*r*r;//计算圆面积并赋值给变量area return area;//返回计算后的圆面积 } public float getCircumference(float r) { float circumference=2*PI*r; //计算圆周长并赋值给变量circumference return circumference; //返回计算后的圆周长 } public static void main(String[] args) { Cire c = new Cire(); float f = c.getArea(2.0f); System.out.println(Float.toString(f)); } } |
在类的继承中,只能做单重继承,而实现接口时,一次则可以实现多个接口,每个接口间使用逗号“,”分隔。这时就可能出现常量或方法名冲突的情况,解决该问 题时,如果常量冲突,则需要明确指定常量的接口,这可以通过“接口名.常量”实现。如果出现方法冲突时,则只要实现一个方法就可以了。下面通过一个具体的 实例详细介绍以上问题的解决方法。
旧的商业模式已然涅槃,高度协作、通力创新的新型商业模式正在浴火重生。现在,任何行业、任何职位的人,都需要具备创造性解决问题的能力。
更具挑战的是,在这个永不停息的商业环境中,你的创新能力必须时刻就位,以应对随时扑面而来的需求——而这也正是你的脑袋最容易变“砖”的时候。然而,在一个快速发展的公司中,每天都能获得新的灵感对于你或你的团队来说都是件不太可能的事情,而且寻找灵感的道路也千变万化。出去散个步启发灵感、换换眼前风景或看一集《Shower Principle》【《我为喜剧狂》(30 Rock)第六季第15集《The Shower Principle》,这一集是关于如何用创意打动上司的】,这些方法在我身上从未连续生效过两次。
幸运的是,我发现了一些旁门左道的小技巧,在你无计可施的时候,或许可以帮上忙。
1.拥抱最后1分钟的创新点子
在最后一分钟有时会发生神奇的事情。这时,人们的思维往往更清晰,也更歇斯底里地渴望突破。当deadline逐步逼近而我的团队还挣扎于困境时,我会在最后关头要求队员向整个团队展示他们的任何想法,不管那些想法听起来是多么“不言而喻”或滑稽可笑。“假设你能全权决定这个项目……。”你无法想象当你把这句话告诉一群创新人士时,它所起到的缓解压力的效果。
值得注意的是,这个方法只在最后关头有效,而你必须身先士卒,这样你的团队才能自由地表达想法。你可以尝试在项目的早些时候赋予团队这种自由权,但他们可能并不会相信你而放开手脚去尝试。等你感觉到团队的紧张气氛时,再把这招使出来。
2.找一些你能看得见摸得着的东西
当你的初步方案形成时,找个可触碰的东西放在屋里。比方说,一件可以让你联想到你所构思产品最终形态的工艺品;它能提醒你,前人做过什么,哪些又可改进。再比如,竞争对手的某件产品,一块建筑材料,成品的一个小部件,一本客户群体一致的杂志等等。任何能够从视觉上唤醒你的点子并且让那个点子壮大的东西都是不错的选择。触觉通常能够激发你的大脑功能,而相比你头脑中的模糊概念,一个看得见摸得着的东西更能激发创造力。
3. 归档你的老点子
对了,点子不会那么容易就死,它们对我们所花的心思来说如此宝贵,我们不竭余力寻找任何机会让它们起效。
别随便抛弃它们,你要做的是一边堆积一边继续前进。将自己从老点子中解放出来,继续前进去想些更新、更好和更有建设性的鬼点子。
4.压缩团队至“奋斗”规模
当我的公司规模甚小,小到只有三个联合创始人时,通常其中一个不得不与另外两个争辩,以便让创意之河继续流动。民主是你的最好朋友也可能是你的刻薄叔叔,但至少它能让你站起来,坚守自己所坚信的东西。
不幸的是,当一个公司越做越大,奋斗的精神将趋于消散。当你的创意团队规模变大时,你很容易变得飘飘然,呼吁智囊团为“大团队”而开动大脑。
压缩你的团队至三四个人,便能再次激起雄辩的火花,在创新的过程中挖掘更深的内涵。不妨将自己(和你的同事)逼迫到某个位置,在那儿,大家都明白,你不得不辩护自己的最佳创意,创新持久战才得以进行,正如需求实际上是革新之母一样。
软件项目/产品的质量问题一直困扰软件企业、监理方和甲方,如何预防、发现、治理软件项目/产品质量问题,是目前我国it发展面临巨大的挑战,这也是it发展过程中关注的主要问题。软件企业、甲方和监理方在研发过程中常常要面临很多难题:
(1)质量的概念与定义;(2)软件的质量要素;(3)软件质量评价的准则;(4)iso 9000软件质量体系结构;(5)软件质量保证过程;(6)质量管理大师简介;(7)质量管理的发展历程;
2、软件质量与质量管理
(1)软件质量面临的挑战及模糊认识;(2)软件质量基础;(3)软件发生质量问题的根本原因及对策;(4)软件质量工程体系;(5)软件质量控制方法、模型与工具;(6)软件全面质量管理;
3、软件质量管理工具选型;(1)软件质量管理粒度分析;(2)软件质量管理工具决策分析;(3)介绍商用质量管理工具;(4)介绍开源质量管理工具;
4、质量的防范策略
(1)质量预防的哲学;(2)为什么担心质量;(3)发布有质量问题产品的商业影响;(4)生命周期成本计算概念;(5)质量防范计划;(6)pareto分析;(7)趋势分析;
5、高质量的软件需求
(1)需求的概念;(2)需求开发的主要困难与应对;(3)需求调查、需求分析的质量控制;(4)什么是合格的软件需求规格说明书;(5)需求验证与管理;(6)需求阶段度量技术及相应的工具;
6、提高软件设计质量
(1)软件设计关键问题分析;(2)软件设计策略方法;(3)软件设计质量控制要点及评价标准;(4)典型系统架构、应用策略及对质量的影响;(5)软件设计质量的分析与评价,方法、技术和工具;
7、高质量编程
(1)编程面临的问题;(2)高质量代码的特性;(3)代码风格与编程规则;(4)关键的编程决策与编程质量;(5)提高程序质量的技术及度量技术与工具;(6)代码审查、
单元测试的质量控制;(7)调整代码达成质量目标;
(1)测试的常识与道理;(2)测试的现实;(3)测试方法应用之道;(4)测试目标实现的完整性和有效性;(5)测试过程的评审和质量保证;(6)软件测试组织和管理;(7)软件测试质量的量化质量管理技术与工具
9、软件发布和维护的质量管理
(1)软件构建(build)健康质量分析;(2)软件发布质量标准定义;(3)软件发布质量管理;(4)软件维护质量管理;
10、软件产品质量评价与选择
(1)软件产品的质量模型(勃姆与麦考尔模型);(2)软件产品质量的度量方法;(3)软件产品评价准则的定义;(4)
微软软件质量测试常用度量;
11、软件度量技术
(1)软件度量概述;(2)软件测量技术基础;(3)“目标驱动”的软件度量;(4)软件规模度量及
工作量估算;(5)面向功能设计(结构)的度量;(6)软件测试相关度量;(7)软件质量度量;
12、缺陷度量
(1)软件质量属性与度量;(2)理解与缺陷相关的各种度量数据;(3)使用缺陷度量数据做决策;(4)缺陷分布度量、缺陷密度、缺陷注入率、整体缺陷清除率与阶段性缺陷清除率;(5)缺陷报告的质量;(6)缺陷分析工具及实践;
13、测试的度量
(1)
测试用例的深度、质量和有效性;(2)测试执行的效率和质量;(3)缺陷报告的质量;(4)测试覆盖度(测试整体的质量);(5)测试环境的稳定性或有效性;
14、成熟度度量(maturity metrics)
(1)组织度量;(2)资源度量;(3)培训度量;(4)文档标准化度量;(5)数据管理与分析度量;(6)过程质量度量;
15、管理度量(management metrics)
(1)
项目管理度量(如里程碑管理度量、风险度量、作业流程度量、控制度量等);(2)质量管理度量(如质量审查度量、质量测试度量、质量保证度量等);(3)
配置管理度量(如式样变更控制度量、版本管理控制度量等);(4)个人能力成熟度度量;(5)团队能力成熟度度量;
16、
软件开发项目规模度量(size measurement)
(1) 功能点分析(fpa:function points analysis);(2) 代码行(loc:lines of code);(3) 德尔菲法(delphi technique);(4) cocomo模型;
方法一:
既然是脚本串行执行,那在场景设计中必然是要用多个脚本,要注意的是需要将Scenario Schedule中的Schedule by设置为Group的模式.然后按实际需要依次设置每个脚本的Schedule.要事先计算好每个脚本的整个执行时间,方便定义后续脚本的开始时间(设置Start Group).
方法二:
使用定时任务执行:
首先创建并设置好要跑的个
测试场景,再创建一个一个批处理程序按先后顺序调用这几个个场景进行测试,最后通过
Windows的定时任务设定批处理的执行时间
写一个批处理文件
批处理示例如下:
cls SET M_ROOT="D:\Program Files\MI\Mercury LoadRunner\bin\" %M_ROOT%\wlrun.exe -TestPath "D:\Program Files\MI\Mercury LoadRunner\scenario\Test\TestScen_1.lrs" -Run %M_ROOT%\wlrun.exe -TestPath "D:\Program Files\MI\Mercury LoadRunner\scenario\Test\TestScen_2.lrs" -Run %M_ROOT%\wlrun.exe -TestPath "D:\Program Files\MI\Mercury LoadRunner\scenario\Test\TestScen_3.lrs" -Run |
这种方式比较灵活,但需要注意在Result Settings中设置“Automatically create a results directory for each scenario execution”,以免后面的测试结果覆盖了前面的。
补充:
如果想做脚本的定时执行,其实也可以用多场景这种方式实现
1.添加要测试的场景A
2.添加一个跟测试无关的场景B,该场景里面思考时间设置自己设置,尽可能设计得能撑到自己想跑脚本的那个时间段
3.设置脚本串行执行,先执行B,执行多长时间后(此时长自己定义,基本是这个时长结束后就是去执行自己要定点执行的A场景)
4.当然最直接的办法就是用定时任务去执行自己的场景,这样就不需要用多场景了。
Selenium 是啥?
Selenium RC是啥?
Webdriver 又是啥?
RC 和 Webdriver 是啥关系?
Webdriver 和编程语言啥关系?
Selenium 能并行执行脚本嘛?
这里虫师用简单方式,告诉你,他们错综复杂的关系。理顺了它们之间的关系才能真正使用它。
Selenium 是什么?
Selenium 是
web自动化测试工具集,包括IDE、Grid、RC(selenium 1.0)、WebDriver(selenium 2.0)等。
Selenium IDE 是firefox浏览器的一个插件。提供简单的脚本录制、编辑与回放功能。
Selenium Grid 是用来对测试脚步做分布式处理。现在已经集成到selenium
server 中了。
RC和WebDriver 更多应该把它看成一套规范,在这套规范里定义客户端脚步与浏览器交互的协议。以及元素定位与操作的接口。
WebDriver是什么?
对于刚接触selenium自动化测试的同学来说不太容易理解API是什么,它到底和编程语言之是什么关系。
http://www.w3.org/TR/2013/WD-webdriver-20130117/
当初,在刚学selenium (webdriver)的时候花了一个星期来翻译这个文档,后来也没弄明白,它是啥。其实它就是一层基础的协议规范。
假如说:Webdriver API(接口规范)说,我们要提供一个页面元素id的定位方法。
require "selenium-webdriver" #导入ruby版的selenium(webdriver) find_element(:id, "xx") #id定位方法 |
C#的webdriver模块是这么实现的:
using OpenQA.Selenium; using OpenQA.Selenium.Firefox; //导入C#版的selenium(webdriver) FindElement(By.Id("xx")) //id定位方法 |
python的webdriver模块是这么实现的:
from selenium import webdriver #导入python版的selenium(webdriver) find_element_by_id("xx") #id定位方法 |
import org.openqa.selenium.*; import org.openqa.selenium.firefox.FirefoxDriver;//导入java版的selenium(webdriver) findElement(By.id("xx")) //id定位方法 Robot Framework + selenium |
因为Robot Framework 对于底层过于封装,所以,我们看不到语言层面的方法定义。所以,Robot Framework 提供给我们的方法如下:
1、导入Robot Framework 版本的selenium(webdriver)
2、使用id方法
Click element
Id=xx
需要说明的是 webdriver API 只提供了web页面操作的相关规范,比如元素定位方法,浏览器操作,获取web页元素属性等。
Webdriver 如何组织和执行用例?
对不起,webdriver 不会。
把写好这些操作页面元素的方法(用例)组织起来执行并输入测试结果,是由编程语言的单元测试框架去完成的。如java 的junit和testng单元测试框架,python 的unittest单元测试框架等。
Selenium RC 和WebDriver 什么关系?
RC和 WebDriver 类似,都可以看做是一套操作web页面的规范。当然,他们的工作原理不一样。
selenium RC 在浏览器中运行 JavaScript 应用,使用浏览器内置的 JavaScript 翻译器来翻译和执行selenese 命令(selenese 是 selenium 命令集合) 。
WebDriver 通过原生浏览器支持或者浏览器扩展直接控制浏览器。WebDriver 针对各个浏览器而开发,取代了嵌入到被测 Web 应用中的 JavaScript。与浏览器的紧密集成支持创建更高级的测试,避免了JavaScript 安全模型导致的限制。除了来自浏览器厂商的支持,WebDriver 还利用操作系统级的调用模拟用户输入。
看样子webdriver 更牛B一些。为了保持向兼容,所以selenium 2.0中,RC 和webdriver 并存,但说起selenium 2.0 一般指的是webdriver 。
并行与分布式的区别
有同学好奇如何并行的执行测试用例,并行要求“同时”执行多条用例,这个也是由编程语言的多线程技术实现的。
你会问Selenium Grid 不是可以实现分布式执行么? 分布式的概念是写好一条用例可以调用不同的平台执行,如 A电脑上有一个测试用例,可以调用B电脑(linux)的 Firefox浏览器来跑A电脑上的测试用例;也可以调用C电脑(windows)的 Chrome浏览器来跑A电脑上的测试用例。这是分布式的概念。
Selenium如何能做移动端测试么?
这里我们以python 语言为例。
from selenium import webdriver
driver= webdriver.Chrome() #获取浏览器驱动。拿到浏览器驱动driver 才能操作浏览器所打找的页面上的元素。
我们把驱动展开是这样的
from selenium import webdriver driver = webdriver.Remote( command_executor='http://127.0.0.1:4444/wd/hub', desired_capabilities={'platform': 'ANY', 'browserName':chrome, 'version': '', 'javascriptEnabled': True }) |
驱动里包含了一些参数,代理服务器(URL)平台,浏览器 ,浏览器版本等。
移动端的自动化测试工具Appium
从本质上来讲,appium同样继承了WebDriver API的接口规范。Appium 同样是支持多种编程语言的。这里仍然以python 为例子。
from appium import webdriver #导入python版的 appium(webdriver)模块
#定义驱动的参数 desired_caps = {} desired_caps['platformName'] = 'Android' desired_caps['platformVersion'] = '4.2' desired_caps['deviceName'] = 'Android Emulator' desired_caps['appPackage'] = 'com.android.calculator2' desired_caps['appActivity'] = '.Calculator' driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) |
这一次因为我们操作的是移动端的安卓。所以我们驱动的参数里就要指定平台是'Android' ,版本是4.2 等信息。拿到驱动后,就可以操作安卓上的APP了。
由于
测试和开发各自所站角度的不同导致了,大家对同一个问题看法的不同,继而导致做法上存在各自的差异,有的时候会因为一个或者两个有争议的问题吵得不可开交,其实都是没有必要的。
在现实的
工作中却是会存在的,如果真的遇到了这些问题,在流程正规的公司里解决起来是比较容易的,在流程混乱的公司里则比较难办。区别在于:一旦遇到了有争议的地方,通常的做法都是选择将相关有争议的人员召集起来大家一起开个会,然后各种阐述自己的主张,这时候大家通常会在会议上商量一个解决争议问题的机制。例如,开发将问题转交给需求设计人员,让其进行判断到底是采取开发的意见还是测试的意见,这里的话,有个不容忽视的点,那就是成本的控制。
纵然测试提出的意见很具有前瞻性和从产品设计的角度来说,能够提高产品总的竞争力,由于系统上线的时间,和开发处理问题等进度各方面的综合考虑,可能测试的意见也会在当前的版本内不予考虑,或许会加入到下一个版本考虑范畴内,从这点来说,测试提出的意见是有价值的;还有可能会出现需求设计人员的误判,本身是一个很好的提议,由于需求设计人员没有意识到自己设计出的需求文档方面的漏洞而断然去拒绝一个好的提议,在这种时候,作为测试人员自身的素质是否达标就能够充分体现出来了,有的测试人员之间就不在坚持自己的主张,感觉反正是需求人员设计的,他们说好就是好,说不好就是不好,完全没有自己任何的思想,而那些素质过硬的测试人员,他们会进一步去思考,他们提出的方案是否值得他们去坚持,如果要坚持自己的主张,那么坚持的依据是什么,要是放弃自己的主张,放弃的依据又在哪里?当发现是需求设计人员的错误时,这时素质过硬的测试人员会采取更加巧妙的方式和方法去和需求设计人员沟通和交流,沟通和交流看似好像就是和对方阐述一下自己的观点,实际上难度却比较大,当我们去赞同别人的时候,会很快拉进我们和对方的亲近感,反之去否定对方的时候,却很容易拉大和对方的代沟,导致无法说服对方,达成共识!后续的合作可能会因不断积累的代沟,导致产生不同程度的芥蒂,造成事情处理起来不是那样的顺畅。
流程混乱的公司会出现的问题就是开发和测试可能会相互的指责,开发感觉测试玩命的测他系统,会想着让测试按照他们的操作手法来使用系统,而测试人员这时也觉得很委屈,如果说只能按照开发人员设计出来的方法使用系统,那还不如让开发自测系统算了,还要测试干嘛,不是无形中浪费公司测试资源吗!特别对于一些找出的开发无法修复的问题,态度不好的开发,很有可能会和测试人员吵起来,其实可能很多人会疑问,那为什么不去制定相应的流程进行规范这些事情?流程的制定是一件很简单的事情,难的是如何让流程能够走下去,之所以混乱是由于缺乏将流程贯彻到底的决心和脚踏实地的执行力,如果你此刻身处这种流程混乱的环境里,那么恭喜你,这正好是能够磨练你尽快成长的训练基地,会让你在无形中思考问题不得不去全面,流程如果过于混乱,一般产品的质量基本都是没有什么保障的,要求做完测试人员要把系统所以可能的风险在测试的时候都得注意到,只要这样,才能避免上线后出的问题不是由于测试人员的漏测导致的,而是源于其他问题引起的。
当你不断的在工作中,遇到有争议的问题首先想到的都是去找相应的依据支持自己的观点或者否定掉自己的观点后,你所提的建议就会更具有价值,你自身的素质也就会不断的得以提高!