qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

操作系统的基本架构

一个普通的操作系统,即类似于一个软件中间件,是用户程序和硬件之间的“中介”
  因此,一个达到要求的操作系统,便能够运行许多用户级别的应用程序(第三方程序)。
  同时,这些应用程序可以:
  - 和用户交互
  - 和互联网交互
  - 和文件系统交互
  对于应用程序来说,操作系统就像是计算机本身,这台“计算机”提供了:
  (1)进程(应用程序的可执行单元);
  (2)内存;
  (3)文件系统(位于磁盘中);
  (4)互联网交互;
  (5)用户交互设备(即输入输出设备,常见的诸如键盘,鼠标,显示器等,专用的设备诸如空间球,游戏手柄等);
  (6)对于多用户操作系统,还需要给每个用户分配权限,管理他们可以操作等文件等;
  (7)计算机安全,比如防止黑客入侵某个部分,或者防止第三方应用程序的肆意破坏= =;
  (8)其它。(Whatever needed)
  (日后补充操作系统的架构概念图)
  因此,当我们学习操作系统时,可以从一下几个方面入手:
  (1)内存管理与CPU:
  首当其冲,这是操作系统,也就是应用程序所谓的“计算机”的核心。
  (2)进程与线程:
  这是最重要的部分,事实上和内存管理、CPU唇齿相连。没有了进程和线程,操作系统也就失去了意义。
  (3)文件系统:
  对于一个操作系统,管理磁盘也相当重要;进程相关的内容都存储在内存中,可是进程需要读写,需要长期存储一些东西,这些都是文件系统都功劳。
  (4)硬件设备接口:
  硬件设备,包括磁盘,鼠标,键盘等,是操作系统的另一个重要任务;对于磁盘,关系到文件系统的读取;而对于其它设备,关系到操作系统能否良好地提供一个交互式应用程序环境。
  (5)互联网接口:
  这关系到操作系统能否提供一个良好的网络应用程序环境。面对互联网发达的今天,没有一个操作系统可以不提供这样的接口。
  (注:Chromium OS,Firefox OS,几乎完全依赖与网络。Web OS,我认为是以后操作系统发展的方向)
  在有了以上几个部分以后,其实操作系统已经可以运行。但是为了更好的服务与应用程序,操作系统需要这些:
  *虚拟内存:可以在磁盘中扩张内存。当应用程序需要的内存大于物理内存时,操作系统可以不受束缚的提供更大的内存。
  *计算机安全:为了保护操作系统、硬盘、用户资料或者别的用户进程,操作系统应该需要提供安全保护。

posted @ 2014-03-31 11:45 顺其自然EVO 阅读(269) | 评论 (0)编辑 收藏

NodeJs连接Mysql数据库

 安装node-mysql:
  npm install mysql@2.0.0-alpha9
  npm install node-mysql
  node程序启动文件app.js:
var express = require('express');
var mysql = require('mysql');
var app = express();
app.use(function(req, res, next){
console.log('%s %s', req.method, req.url);
next();
});
var conn = mysql.createConnection({
host:'localhost',
user:'root',
database:'nodedb',
password:'********',
port:3306
});
conn.connect();
app.get('/', function(req, res){
conn.query('SELECT * from news', function(err, rows, fields) {
if (err) throw err;
var data = '';
for(var i = 0; i < rows.length; i++){
data += '<p>' + 'ID:' + rows[i].id + '</p>';
data += '<p>' + 'title:' + rows[i].title + '</p>';
data += '<p>' + 'contents:' + rows[i].contents + '</p>';
data += '<hr />';
}
res.send(data);
});
});
app.listen(3000);
console.log('Listening on port 3000');

posted @ 2014-03-31 11:44 顺其自然EVO 阅读(423) | 评论 (0)编辑 收藏

JVM(Java虚拟机)优化大全和案例实战

  堆内存设置
  原理
  JVM堆内存分为2块:Permanent Space 和 Heap Space。
  Permanent 即 持久代(Permanent Generation),主要存放的是Java类定义信息,与垃圾收集器要收集的Java对象关系不大。
  Heap = { Old + NEW = {Eden, from, to} },Old 即 年老代(Old Generation),New 即 年轻代(Young Generation)。年老代和年轻代的划分对垃圾收集影响比较大。
  年轻代
  所有新生成的对象首先都是放在年轻代。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代一般分3个区,1个Eden区,2个Survivor区(from 和 to)。
  大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当一个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当另一个Survivor区也满了的时候,从前一个Survivor区复制过来的并且此时还存活的对象,将可能被复制到年老代。
  2个Survivor区是对称的,没有先后关系,所以同一个Survivor区中可能同时存在从Eden区复制过来对象,和从另一个Survivor区复制过来的对象;而复制到年老区的只有从另一个Survivor区过来的对象。而且,因为需要交换的原因,Survivor区至少有一个是空的。特殊的情况下,根据程序需要,Survivor区是可以配置为多个的(多于2个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
  针对年轻代的垃圾回收即 Young GC。
  年老代
  在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
  针对年老代的垃圾回收即 Full GC。
  持久代
  用于存放静态类型数据,如 Java Class, Method 等。持久代对垃圾回收没有显著影响。但是有些应用可能动态生成或调用一些Class,例如 Hibernate CGLib 等,在这种时候往往需要设置一个比较大的持久代空间来存放这些运行过程中动态增加的类型。
  所以,当一组对象生成时,内存申请过程如下:
  JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。
  当Eden区空间足够时,内存申请结束。否则执行下一步。
  JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中活跃对象放入Survivor区。
  Survivor区被用来作为Eden区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被移到年老代。
  当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
  Full GC后,若Survivor区及年老代仍然无法存放从Eden区复制过来的对象,则会导致JVM无法在Eden区为新生成的对象申请内存,即出现“Out of Memory”。
  OOM(“Out of Memory”)异常一般主要有如下2种原因:
  1. 年老代溢出,表现为:java.lang.OutOfMemoryError:Javaheapspace
  这是最常见的情况,产生的原因可能是:设置的内存参数Xmx过小或程序的内存泄露及使用不当问题。
  例如循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存。还有的时候虽然不会报内存溢出,却会使系统不间断的垃圾回收,也无法处理其它请求。这种情况下除了检查程序、打印堆内存等方法排查,还可以借助一些内存分析工具,比如MAT就很不错。
  2. 持久代溢出,表现为:java.lang.OutOfMemoryError:PermGenspace
  通常由于持久代设置过小,动态加载了大量Java类而导致溢出,解决办法唯有将参数 -XX:MaxPermSize 调大(一般256m能满足绝大多数应用程序需求)。将部分Java类放到容器共享区(例如Tomcat share lib)去加载的办法也是一个思路,但前提是容器里部署了多个应用,且这些应用有大量的共享类库。  参数说明
  -Xmx3550m:设置JVM最大堆内存为3550M。
  -Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
  -Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。
  -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
  -XX:NewSize=1024m:设置年轻代初始值为1024M。
  -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
  -XX:PermSize=256m:设置持久代初始值为256M。
  -XX:MaxPermSize=256m:设置持久代最大值为256M。
  -XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。
  -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。
  -XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
  疑问解答
  -Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3组参数都可以影响年轻代的大小,混合使用的情况下,优先级是什么?
  如下:
  高优先级:-XX:NewSize/-XX:MaxNewSize
  中优先级:-Xmn(默认等效  -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
  低优先级:-XX:NewRatio
  推荐使用-Xmn参数,原因是这个参数简洁,相当于一次设定 NewSize/MaxNewSIze,而且两者相等,适用于生产环境。-Xmn 配合 -Xms/-Xmx,即可将堆内存布局完成。
  -Xmn参数是在JDK 1.4 开始支持。
  垃圾回收器选择
  JVM给出了3种选择:串行收集器、并行收集器、并发收集器。串行收集器只适用于小数据量的情况,所以生产环境的选择主要是并行收集器和并发收集器。
  默认情况下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行智能判断。
  串行收集器
  -XX:+UseSerialGC:设置串行收集器。
  并行收集器(吞吐量优先)
  -XX:+UseParallelGC:设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。
  -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收。此值建议配置与CPU数目相等。
  -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0开始支持对年老代并行收集。
  -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间(单位毫秒)。如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。
  -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代Eden区大小和Survivor区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标。此参数建议在使用并行收集器时,一直打开。
  并发收集器(响应时间优先)
  -XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。CMS收集是JDK1.4后期版本开始引入的新GC算法。它的主要适合场景是对响应时间的重要性需求大于对吞吐量的需求,能够承受垃圾回收线程和应用线程共享CPU资源,并且应用中存在比较多的长生命周期对象。CMS收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
  -XX:+UseParNewGC:设置年轻代为并发收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此参数。
  -XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩和整理,所以运行一段时间并行收集以后会产生内存碎片,内存使用效率降低。此参数设置运行0次Full GC后对内存空间进行压缩和整理,即每次Full GC后立刻开始压缩和整理内存。
  -XX:+UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片。
  -XX:+CMSIncrementalMode:设置为增量收集模式。一般适用于单CPU情况。
  -XX:CMSInitiatingOccupancyFraction=70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。
  其它垃圾回收参数
-XX:+ScavengeBeforeFullGC:年轻代GC优于Full GC执行。
-XX:-DisableExplicitGC:不响应 System.gc() 代码。
-XX:+UseThreadPriorities:启用本地线程优先级API。即使 java.lang.Thread.setPriority() 生效,不启用则无效。
-XX:SoftRefLRUPolicyMSPerMB=0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。
-XX:TargetSurvivorRatio=90:允许90%的Survivor区被占用(JVM默认为50%)。提高对于Survivor区的使用率。
  辅助信息参数设置
-XX:-CITime:打印消耗在JIT编译的时间。
-XX:ErrorFile=./hs_err_pid.log:保存错误日志或数据到指定文件中。
-XX:HeapDumpPath=./java_pid.hprof:指定Dump堆内存时的路径。
-XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存。
-XX:OnError=";":出现致命ERROR后运行自定义命令。
-XX:OnOutOfMemoryError=";":当首次遭遇内存溢出时执行自定义命令。
-XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆内存中类实例的柱状信息,同JDK的 jmap -histo 命令。
-XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印线程栈中并发锁的相关信息,同JDK的 jstack -l 命令。
-XX:-PrintCompilation:当一个方法被编译时打印相关信息。
-XX:-PrintGC:每次GC时打印相关信息。
-XX:-PrintGCDetails:每次GC时打印详细信息。
-XX:-PrintGCTimeStamps:打印每次GC的时间戳。
-XX:-TraceClassLoading:跟踪类的加载信息。
-XX:-TraceClassLoadingPreorder:跟踪被引用到的所有类的加载信息。
-XX:-TraceClassResolution:跟踪常量池。
-XX:-TraceClassUnloading:跟踪类的卸载信息。
  关于参数名称等
  标准参数(-),所有JVM都必须支持这些参数的功能,而且向后兼容;例如:
  -client——设置JVM使用Client模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或开发调试;在32位环境下直接运行Java程序默认启用该模式。
  -server——设置JVM使Server模式,特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境。在具有64位能力的JDK环境下默认启用该模式。
  非标准参数(-X),默认JVM实现这些参数的功能,但是并不保证所有JVM实现都满足,且不保证向后兼容;
  非稳定参数(-XX),此类参数各个JVM实现会有所不同,将来可能会不被支持,需要慎重使用;  参数说明
  -Xmx3550m:设置JVM最大堆内存为3550M。
  -Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
  -Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。
  -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
  -XX:NewSize=1024m:设置年轻代初始值为1024M。
  -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
  -XX:PermSize=256m:设置持久代初始值为256M。
  -XX:MaxPermSize=256m:设置持久代最大值为256M。
  -XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。
  -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。
  -XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
  疑问解答
  -Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3组参数都可以影响年轻代的大小,混合使用的情况下,优先级是什么?
  如下:
  高优先级:-XX:NewSize/-XX:MaxNewSize
  中优先级:-Xmn(默认等效  -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
  低优先级:-XX:NewRatio
  推荐使用-Xmn参数,原因是这个参数简洁,相当于一次设定 NewSize/MaxNewSIze,而且两者相等,适用于生产环境。-Xmn 配合 -Xms/-Xmx,即可将堆内存布局完成。
  -Xmn参数是在JDK 1.4 开始支持。
  垃圾回收器选择
  JVM给出了3种选择:串行收集器、并行收集器、并发收集器。串行收集器只适用于小数据量的情况,所以生产环境的选择主要是并行收集器和并发收集器。
  默认情况下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行智能判断。
  串行收集器
  -XX:+UseSerialGC:设置串行收集器。
  并行收集器(吞吐量优先)
  -XX:+UseParallelGC:设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。
  -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收。此值建议配置与CPU数目相等。
  -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0开始支持对年老代并行收集。
  -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间(单位毫秒)。如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。
  -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代Eden区大小和Survivor区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标。此参数建议在使用并行收集器时,一直打开。
  并发收集器(响应时间优先)
  -XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。CMS收集是JDK1.4后期版本开始引入的新GC算法。它的主要适合场景是对响应时间的重要性需求大于对吞吐量的需求,能够承受垃圾回收线程和应用线程共享CPU资源,并且应用中存在比较多的长生命周期对象。CMS收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
  -XX:+UseParNewGC:设置年轻代为并发收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此参数。
  -XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩和整理,所以运行一段时间并行收集以后会产生内存碎片,内存使用效率降低。此参数设置运行0次Full GC后对内存空间进行压缩和整理,即每次Full GC后立刻开始压缩和整理内存。
  -XX:+UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片。
  -XX:+CMSIncrementalMode:设置为增量收集模式。一般适用于单CPU情况。
  -XX:CMSInitiatingOccupancyFraction=70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。
  其它垃圾回收参数
-XX:+ScavengeBeforeFullGC:年轻代GC优于Full GC执行。
-XX:-DisableExplicitGC:不响应 System.gc() 代码。
-XX:+UseThreadPriorities:启用本地线程优先级API。即使 java.lang.Thread.setPriority() 生效,不启用则无效。
-XX:SoftRefLRUPolicyMSPerMB=0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。
-XX:TargetSurvivorRatio=90:允许90%的Survivor区被占用(JVM默认为50%)。提高对于Survivor区的使用率。
  辅助信息参数设置
-XX:-CITime:打印消耗在JIT编译的时间。
-XX:ErrorFile=./hs_err_pid.log:保存错误日志或数据到指定文件中。
-XX:HeapDumpPath=./java_pid.hprof:指定Dump堆内存时的路径。
-XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存。
-XX:OnError=";":出现致命ERROR后运行自定义命令。
-XX:OnOutOfMemoryError=";":当首次遭遇内存溢出时执行自定义命令。
-XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆内存中类实例的柱状信息,同JDK的 jmap -histo 命令。
-XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印线程栈中并发锁的相关信息,同JDK的 jstack -l 命令。
-XX:-PrintCompilation:当一个方法被编译时打印相关信息。
-XX:-PrintGC:每次GC时打印相关信息。
-XX:-PrintGCDetails:每次GC时打印详细信息。
-XX:-PrintGCTimeStamps:打印每次GC的时间戳。
-XX:-TraceClassLoading:跟踪类的加载信息。
-XX:-TraceClassLoadingPreorder:跟踪被引用到的所有类的加载信息。
-XX:-TraceClassResolution:跟踪常量池。
-XX:-TraceClassUnloading:跟踪类的卸载信息。
  关于参数名称等
  标准参数(-),所有JVM都必须支持这些参数的功能,而且向后兼容;例如:
  -client——设置JVM使用Client模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或开发调试;在32位环境下直接运行Java程序默认启用该模式。
  -server——设置JVM使Server模式,特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境。在具有64位能力的JDK环境下默认启用该模式。
  非标准参数(-X),默认JVM实现这些参数的功能,但是并不保证所有JVM实现都满足,且不保证向后兼容;
  非稳定参数(-XX),此类参数各个JVM实现会有所不同,将来可能会不被支持,需要慎重使用;

posted @ 2014-03-31 11:43 顺其自然EVO 阅读(661) | 评论 (0)编辑 收藏

关于软件测试的几点反思 - 关于测试团队的组织

 这一篇是系列文章的第三篇,前面两篇分别谈了测试的必需性《关于软件测试的几点反思 - 测试是必需的吗?》,以及测试工作的一些内容《关于软件测试的几点反思 - 测试工作的三个阶段》,接下来想聊一下测试团队的组织。
  要讨论这个话题,首先要讨论下测试人员本身的归属,因为通常是人多了才有组织的必要,很多东西都是一点点长出来的。
  我在读研期间实习的一家公司,根本没有专职的测试人员,回头想想当时还是挺大胆的,因为做的是比较核心的系统,而且当时像我这种实习生都写了很多核心的涉及金额计算的代码,然后大家自测下就上线了。这种情况也持续了好久,也验证了不一定必需,在特定的情况下。
  个人的工作经历,没有一开始去很小的研发组织。后面工作后的面试中,也接触过很多规模较小的公司的测试人员,这种情况下大部分是直接归属到项目,汇报给开发经理。人数少,大部分是比较基础的黑盒测试,相对也比较弱势。没有任何贬低的意思,但是客观来说,这个阶段的测试很难有一些比较深入的测试技术上的实践,时间不允许,也处于没有人带的情况,大家基本上都专注在项目的功能测试上面。一直觉得环境对人的影响是比较大的。有些比较有上进心的同学会自己学一些技术,但是因为没有指导,也没有实际应用的场景,通常比较浅。
  后面等到整个研发体系发展大了之后,可能测试人员也慢慢多了起来,同时服务于多个开发小组,于是就出现了测试团队的二级组织。比如对口每个开发小组的有几个人,或者更多,然后有一个lead或者first line manager,然后这些人汇总到一个second line的manager。这个时候随着测试团队规模的壮大,当然也是随着整个研发组织的壮大,以及业务和开发方面提出了更多更高的质量要求,测试团队在客观上有了进一步提升的需求。主观上因为second line的出现,会不再满足于完成基本的测试工作,也有提升的动力。一些测试的规范,用例设计缺陷管理,数据的分析,工具平台的引入,自动化的开展等慢慢开始引入。
  接下来,可能到研发部门层面有一个完整的测试团队,进而可能是整个公司,或者事业部(BG,BU)层面有一个完整的测试部门,或者中心。
  目前看到的腾讯和阿里的几个大的业务导向的事业部都是这样的情况,比如腾讯的互联网,互动娱乐,电商(曾经);阿里的淘宝,天猫和支付宝,都是在事业部层面有一个完整的测试团队。
  进一步,如果这类很大型的公司,把全集团的测试集中到一起的还没看到,主要是因为组织架构的顶层还是按业务来划分的,比如事业部制。
  基于上面的讨论,我们可以从测试的集中这种角度来看看测试团队的情况,这样划分就有两种,集中还是不集中。
  集中的例子就是上面说的情况,所有专职测试人员都在一个小组,一个大的二级组,进而一个中心,一个部门,数据结构上是一颗树。非集中的情况就是测试人员散落在不同的开发中心,或者开发组,是一个森林。每一块的测试人员组成一个小组,汇报给开发中心的总监,或者开发经理。 目前了解到不少公司是这样来组织的。
  每一种组织方式都是结合了公司的实际情况和要求,但是如果单纯从测试团队的价值和效率方面,目前来看,会觉得集中到一起是个更好的方式,主要的理由是下面几点。
  1. 集中到一起之后,因为资源的整合,可以减少各个团队的重复建设,集中来做一些平台建设,技术研究,或者横行的技术共享,有利于提升团队的技术深度。
  2. 从业务的角度,集中后测试可以横向的对齐,来看各个项目的质量情况,研发流程的过程执行和效率的情况。从整个组织的角度,对研发的质量和效率有促进的作用。
  3. 从测试人员个人发展的角度,因为整个测试组织有了更好的深度,个人发展的空间也会更大,无论技术还是管理方面。
  4. 测试人员的归属感,有自己的部门和自己的职业发展通道。
  谈到和项目的对应,目前采用测试集中方面的团队也基本上是矩阵式管理,特别是针对负责业务测试的小组。一方面,从组织架构上是归属于质量部或者质量中心,但是从日常工作上,是归属到项目或者产品,和对应的产品、开发团队密切配合,包括座位可能都在一起。
  就目前观察的情况来看,这种组织架构相对是比较成熟,也比较普遍被采用的,运作起来也还比较顺畅。


 第二个方面,想讨论一下测试人员的内部细分。IT行业早已经是内部就开始隔行如隔山,分得非常细了。考虑对口的产品和技术形态,测试人员也有很大的差异了,比如测试通信设备的,测试Web网站的,以及测试app的,其背景知识,工作流程也有很大的差异。即使不考虑这方面,从专注的测试工作内容上看,也有进一步的细分。我接触到的会分为四个方面:
  1. 业务测试
  这一部分的测试人员就是上面提到的矩阵式管理的那一类,负责具体的产品的测试和质量提升。所以这一类的人就数量上来说通常是最多的。
  2. 专项测试
  如果测试组织稍大,对测试的深度有更高的要求,而有些测试类型又需要比较长时间的积累,且技能有横向的共用性,那么就可能有专人来做,俗称专项。比如安全测试,性能测试等。就实际运作的情况来看,像安全测试这种看起来确实比较有必要,因为没有长时间专注的积累难以有效果。这样的专项测试通常人数不是很多。
  3. 质量管理
  有些地方叫QA或者SQA,就是第二篇里面提到的专注在质量数据的收集,研发流程的管理和推动等事项上面的一些人。通常放在测试部门,但是也可能放在研发管理等团队。
  4. 测试开发
  当整个测试的团队比较大,需要很多共性的基础的平台和工具建设,就可能会抽出一个团队专门来做这方面的事情,称之为测试开发。
  实际中,关于业务测试和测试开发,其实有时候界限是没有那么清楚的,取决于多个方面的因素:
  1. 老大观念
  没办法,这个很重要。接触到几位部门级测试团队的负责人,观念不完全一样,有些认为独立的测试开发团队很重要,且愿意大的投入,有些觉得没必要有专门的测试开发团队,业务测试团队应该有能力来做测试技术方面的事情。
  2. 业务测试团队的能力
  这个要看实际的情况,业务测试如果要做得比较好,业务测试的团队本身也需要比较好的技术能力,所以很多测试开发意义上的事情业务测试团队也可以做,实际也在做。但是也有些情况,业务测试团队本身的技术能力不够,或者时间非常的局限。从业务测试团队本身的意愿上,肯定也希望能做一些技术方面的事情。
  3. 测试开发团队和业务测试团队的双向互动的情况
  一方面是看测试开发团队做的东西能否在业务测试团队落地,涉及方案本身,是否贴合项目时间,以及和业务测试团队的配合的情况。这个实际情况应该还是比较复杂的,各个团队面临的实际情况都不太一样。
  最后想讨论下关于测试人员外包。这里不讨论整个项目的测试外包,那是另一个范畴,也曾经和一个资深的测试leader深入聊过,不过最终也只是些理论推导,没有实际的应用,很多问题也不好说。
  自己关于外包的观念也一直在变,因为看到身边不同的实际的例子,目前的想法大致如下:
  1. 觉得外包非常的有帮助,能帮助完成很多的测试工作,在招聘速度上也很有优势,所以是个不错的选择。
  2. 就实际运作来看,觉得外包的比例不能太高,一个正式配1-2个外包应该效果还不错,太多了对项目,对外包同学的成长觉得都不太好。
  在我们团队的应用中,还有几个小的实例的体会
  1. 像面试正式员工一样面试外包
  如果我们觉得外包只是过来做黑盒的手工测试,随便找几个人就好了,可能效果不会太好,因为最终还是取决于人。我们目前的几位外包同学都是我们非常认真的面试过,从很多位中挑选出来的,在团队里面发挥的作用其实已经超出了我们的预期。
  2. 更放手让他去做
  就目前项目的情况,我们的外包同学除了做好基本的黑盒手工测试,他们也可以牵头一个项目的测试跟进发周报,自动化的用例制作和问题定位,crash问题的跟进分析等,而且工作的态度和主动性非常好。因为我们没有给他们设明确的界限,只要他愿意尝试,也具备基本的能力,那就可以去做。这个和上面的1也有关系。
  3. 关注外包同学的发展
  这两年接触了很多外包同学,团队里有好几位正式同学也是之前做过外包,所以对外包的看法有些不同了,在职业发展上尽量和正式同学一样看待,并关注他们个人的发展。
  在这个充斥着企业文化,价值观的年代,测试如果作为一个独立的部门,也一定会被问这样的问题:作为一个独立的测试部门,我们的核心价值是什么?
  这个问题好像一直都会在,值得去想想看。

posted @ 2014-03-31 11:39 顺其自然EVO 阅读(168) | 评论 (0)编辑 收藏

12个最好的免费网站速度和性能测试工具

 如果你是位个人站长,就能理解网站速度的重要性。自从 Google 算法开始使用网页加载时间作为搜索排序参数之后,网站速度对 SEO 的影响非常大。而且,很慢的加载速度会对网站访问者产生消极的影响。如果你的网站加载速度很慢,需要等待一段时间才能加载,那么用户很有可能不会再次访问 这个网站。
  所以,为了解决以上说到的问题,我们收集整理了一个最好的免费网站速度测试和分析工具列表。接下来介绍的工具都是免费,而且会提供非常详细的数据报告给用户,帮助用户做些必要的补救措施。希望大家都能在下面的列表中找到对自己有帮助的,使自己的网站访问速度越来越快!
  1. Google PageSpeed Insights
  Google PageSpeed Insights 允许用户分析网站页面的内容,并且会提供加快网站访问速度的建议。
  2. GT Matrix
  GTmetrix 可以帮助用户开发一个快速,高效,能全面改善用户体验的网站。它会为网站性能打分,然后提供可行性的建议来改善已发现的问题。
  3. Neustar Free Load Testing & Performance Test
  neustar 这个工具是个简单快速生成网站性能分析数据的工具。它能忽略掉大小和地理位置来检测和负载测试网站,非常容易得出网站的性能分析,帮助用户加快网站加载速度。
  4. Web Page Analyzer
  Web Page Analyzer 是个非常强大的速度测试工具,提供详细的网站分析数据并且会提供提高网站性能的建议。它提供大量的 web 页面速度报告,global report,外部文件计算,加载时间,网站分析数据和改善建议。
  5. Pingdom
  Pingdom 是个非常杰出的工具,帮助用户生成大量网站的报告(页面大小,浏览器缓存,性能等级等),确定网站的加载时间,而且允许用户跟踪性能的历史记录,能在不同位置进行网站测试。
  6. Load Impact
  Load Impact 允许用户做些 web 应用的负载和性能测试。它不断增加网站流量来测量网站性能。Load Impact 会选择一个全球负载区,测试模拟客户,带宽,接收数据和每秒请求等。越来越多客户变活跃,这个工具会用个漂亮的图表来展示测量的加载时间。
  7. WebPage Test
  用户可以使用 WebPage Test 来进行简单的测试,又或者是进行高级的测试,比如多步事物处理,视频采集,内容屏蔽等。测试结果会提供丰富的诊断信息,包括资源加载瀑布图表,页面速度优化检测和改善建议等。
  8. Octa Gate Site Timer
  Octa Gate Site Timer 工具允许用户检测每个用户加载一个或多个页面的时间。当页面加载的时候,SiteTimer 存储每个项目加载的数据和用户接收的数据,这些数据会用一个网格来显示。
  9. Which Loads Faster
  Which Loads Faster 是用来测试 web 性能问题的工具,可以在每个用户的浏览器测试。whichloadsfaster 是开源的,使用 HTML 和 JavaScript 编写的测试工具,完全在客户端运行。
  10. Yslow
  YSlow 能分析 web 页面,基于一系列 web 页面高性能规则提供改进网页性能的建议。
  11. Show Slow
  Show Slow 是个开源测试工具,帮助检测各种网站性能指标。它会把页面速度的检测结果排序,有 dynaTrace AJAX 版,WebPageTest 和 YSlow。它会用图形化显示排序结果,帮助用户理解哪些因素会影响网站的性能。
  12. Free Speed Test
  Free Speed Test 是个在线网站速度测试工具,可以通过全球多个数据中心来检测加载时间和网站速度。这可以让用户了解全球各个角落网站的实际加载速度。

posted @ 2014-03-31 11:39 顺其自然EVO 阅读(342) | 评论 (0)编辑 收藏

单元测试之Monkey Patch

  再说monkey patch之前先说下, python中的Test Double, Test Double就是在测试case中给某个对象做替身的意思. 用一个假对象替换.
  用Test Double时, 可以有三种实现的形式, Stub,Mock object, Fake Object, Mock object 在我的另一博文中http://blog.csdn.net/juvxiao/article/details/21562325分析了下, 其他两种比较简单, 可看https://wiki.openstack.org/wiki/SmallTestingGuide了解 ,这个link中还提到Test Double的两种实现方式: 依赖注入 和 monkey patching.
  依赖注入
class FamilyTree(object):
def __init__(self, person_gateway):
self._person_gateway = person_gateway
  可以把person_gateway用一个假对象替换, 从而让测试专注在FamilyTree本身,
person_gateway = FakePersonGateway()
# ...
tree = FamilyTree(person_gateway)
monkey patching
  这种测试只能运行在像python这样的动态语言中, 它通过在运行时替换名空间的方式实现测试。如下例
class FamilyTree(object):
def __init__(self):
self._person_gateway = mylibrary.dataaccess.PersonGateway()
  那我们就可以在测试时把mylibrary.dataaccess.PersonGateway名空间替换为FakeGataway名空间.
mylibrary.dataaccess.PersonGateway = FakePersonGateway
# ...
tree = FamilyTree()
通过一个OpenStack中使用monkey patch的例子来说说, 这个代码片断摘自nova的单元测试test_virt_driver.py,用于讲述monkey patch用法
import nova.tests.virt.libvirt.fake_imagebackend as fake_imagebackend
import nova.tests.virt.libvirt.fake_libvirt_utils as fake_libvirt_utils
import nova.tests.virt.libvirt.fakelibvirt as fakelibvirt
sys.modules['libvirt'] = fakelibvirt
import nova.virt.libvirt.driver
import nova.virt.libvirt.firewall
self.useFixture(fixtures.MonkeyPatch(
'nova.virt.libvirt.driver.imagebackend',
fake_imagebackend))
self.useFixture(fixtures.MonkeyPatch(
'nova.virt.libvirt.driver.libvirt',
fakelibvirt))
self.useFixture(fixtures.MonkeyPatch(
'nova.virt.libvirt.driver.libvirt_utils',
fake_libvirt_utils))
  这个例子中使用了fixtures module(fixtures就是一个testcase助手, 把一些不依赖具体测试的过程提取出来放到fixtures module中, 可以使得测试代码干净)来实现monkey patch, 就是用前几行的fake object 这个名空间替换真正driver object的名空间。达到测试时的狸猫换太子。

posted @ 2014-03-31 11:35 顺其自然EVO 阅读(296) | 评论 (0)编辑 收藏

Web服务性能测试:Node完胜Java

 简介
  我最近做了一些简单的关于内存的Web Service性能测试。我使用Java(REST + SOAP)和Node.js(REST)将一些接口功能缓存起来。跟期望的一样,Node应用的性能远远超出Java。(响应时间至少快1倍以上)。
  译者注*  NodeJS跟许多其他单线程语言一样,对内存并不贪婪,因为没有关于线程的内存开销,内存占用不会随着连接数的增长而增长,尤其在剔除掉读写文件/数 据库等异步操作后,完全基于内存的NodeJS将有更显著的性能提升,从某种意义上来说基于内存的nodejs服务所能支持的最大并发数将仅受限于带宽和 CPU的处理能力。
  可参见: 性能测评:Ngix_Lua, Node.JS Python三者性能相当,均比php快近一倍,  PayPal为什么从Java迁移到Node.js
  缓存应用
  图1:关于缓存应用的原理图。缓存支持插入,获取,删除键/值对
  图2:关于应用更详细的物理图
  这里使用了另外一种形式的REST,cache操作通过HTTP verbs来完成(Insert = PUT, Fetch = GET, Remove = DELETE)。数据刷新通过使用timeouts(Node)和scheduled threads(Java)来完成。缓存冗余是通过服务器间的REST调用来实现。(通过PUT/DELETE)
  对于Java SOAP的扩展,cache操作通过经典的HTTP POST SOAP包来实现。
  应用层结构
  图3:基于Cache的Java REST组织结构图。Apache Tomcat + Jersey (servlet)在这一层。
  图4:基于Cache的Java SOAP组织结构图。Apache Tomcat + Axis2 (servlet)在这一层。
  图5:为Node应用。仅初始化了一个worker。
  测试
  图6: 为我测试时使用的环境
Java + Node REST 缓存插入测试
ab -A username:password -u restput.txt -n 1000 -c 1 https://server/ctispan/rest/key/111 > results.txt
restput.txt
value=test111
  Java SOAP 缓存获取测试
ab -A client:password -T "application/soap+xml; charset=UTF-8" -p soapget.xml -n 1000 -c 1 https://server/ctispan/services/CacheProxyWS.CacheProxyWSHttpSoap11Endpoint/ > results.txt
soapget.xml
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns1:getValue xmlns:ns1="http://server.ctispan.jwisoft.com"><ns1:key>111</ns1:key></ns1:getValue></soapenv:Body></soapenv:Envelope>
  Java SOAP 缓存插入测试
ab -A client:password -T "application/soap+xml; charset=UTF-8" -p soapput.xml -n 1000 -c 1 https://server/ctispan/services/CacheProxyWS.CacheProxyWSHttpSoap11Endpoint/ > results.txt
soapput.xml
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns1:putValue xmlns:ns1="http://server.ctispan.jwisoft.com"><ns1:key>111</ns1:key><ns1:value>text111</ns1:value></ns1:putValue></soapenv:Body></soapenv:Envelope>
  结论
  下面A/B测试生成的一些数据。对于Node对比Java的结果,我并不吃惊。不过对于Java REST和SOAP的对比让我稍微有些吃惊。我原本以为因为SOAP的复杂性,REST的性能应该比SOAP好很多才对。这也许是因为Apache Axis2 API要比Jersey API高效的多造成的吧。

posted @ 2014-03-31 11:07 顺其自然EVO 阅读(358) | 评论 (0)编辑 收藏

MySQL数据库主从同步安装与配置总结

 MySQL的主从同步是一个很成熟的架构,优点为:
  ①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力;
  ②在从主服务器进行备份,避免备份期间影响主服务器服务;
  ③当主服务器出现问题时,可以切换到从服务器。
  所以我在项目部署和实施中经常会采用这种方案.
  + 数据库目录及其它
  my.cnf配置文件     /etc/my.cnf
  mysql数据库位置    datadir=/var/lib/mysql
  主数据库:192.168.2.119
  从数据库:192.168.2.220
  操作系统:RHEL5.x 32位
  服务器类型: 虚拟机
  + mysql5.0.77 安装:
  ① 配置好linux的yum服务后,直接yum -y install mysql即可
  附:安装php\mysql一条命令安装:yum -y install httpd php mysql mysql-server php-mysql
  ② 启动MySQL
  service mysqld start(restart|stop)
  一、设置主库
  1、修改主库my.cnf,主要是设置个不一样的id和logbin(#这部可依具体环境而定,压力大的化可采用huge.cnf)
  [root@localhost etc]#vi /etc/my.cnf
  # 记住这部分一定要配置在[mysqld]后面,否则无法找到从节点,各个配置项的含义可自己查阅文档
[mysqld]
log-bin=mysql-bin
server-id=1
binlog-ignore-db=information_schema
binlog-ignore-db=cluster
binlog-ignore-db=mysql
  2、启动主库生效
  [root@localhost etc]service mysqld restart
  3、登陆主库
  [root@localhost etc]mysql -u root -p
  4、赋予从库权限帐号,允许用户在主库上读取日志
  mysql> grant all privileges on *.* to '用户名'@'%' identified by '密码';
  5、检查创建是否成功
  select user,host from mysql.user;
  6、锁主库表
  mysql> flush tables with read lock;7、显示主库信息
  记录File和Position,从库设置将会用到
mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 98   |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
# 说明,如果执行这个步骤始终为Empty set(0.00 sec),那说明前面的my.cnf没配置对。
  8、另开一个终端登陆220,打包主库迁移数据(如果你使用的yum安装,有默认数据库并未做任何改动,则不需要进行拷贝)
  目的是为了保证两台服务器的mysql数据库一致,这里可以自行tar打包或者使用mysqldump命令备份恢复的方式进行。
  二、设置从库
  1、传输拿到主库包、解包
  # 登陆从库
  从上一步中备份的数据库恢复到220服务器节点上。
  2、在119节点上解锁主库表(对应第一点设置主库中第6步锁主库表的操作)
  mysql> unlock tables;
  3、在220节点上修改从库my.cnf(位置一样)
  # 记住这部分一定要配置在[mysqld]后面,否则无法找到从节点,各个配置项的含义可自己查阅文档
[mysqld]
log-bin=mysql-bin
server-id=2
binlog-ignore-db=information_schema
binlog-ignore-db=cluster
binlog-ignore-db=mysql
replicate-do-db=test
replicate-ignore-db=mysql
log-slave-updates
slave-skip-errors=all
slave-net-timeout=60
master-host=192.168.2.119
master-user=root
master-password=pfingo
  4、在220节点上验证连接主库
  [root@localhost etc]mysql -h 192.168.2.119 -u 用户名 -p
  5、在220节点从库上设置同步
#设置连接MASTER MASTER_LOG_FILE为主库的File,MASTER_LOG_POS为主库的Position
#注意下面第二条命令语句中的master_log_file='mysql-bin.000001', master_log_pos=98;对应为前面在主库中执行的show master status;结果
mysql> slave stop;
mysql> change master to master_host='192.168.2.119',master_user='root',master_password='pfingo',master_log_file='mysql-bin.000001', master_log_pos=98;
mysql> slave start;
  6、启动从库服务
  mysql> slave start;
  7、进行测试
  在主库上的test库上建立名为myTest的表
mysql> CREATE TABLE `myTest` (
`id` INT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
`username` VARCHAR( 20 ) NOT NULL ,
`password` CHAR( 32 ) NOT NULL ,
`last_update` DATETIME NOT NULL ,
`number` FLOAT( 10 ) NOT NULL ,
`content` TEXT NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = MYISAM ;
  在从表中马上看到了效果,主从同步成功了;
  为了更进一步验证在从库上输入show slave status\G;
  mysql> show slave status\G;
  Slave_IO_Running: Yes(网络正常);
  Slave_SQL_Running: Yes(表结构正常)
  进一步验证了以上过程的正确性。

posted @ 2014-03-28 11:24 顺其自然EVO 阅读(234) | 评论 (0)编辑 收藏

Java-了解注解及其应用

一、注解基本了解和应用
  1、何为注解?
  注解就是一种标记,在程序中加了注解就等于加了标记,没加,就没有标记。
  2、注解有何作用?
  加了注解,java编译器、开发工具或是其他程序可以通过反射技术了解你的类或各种元素是否有标记,有什么标记就做什么
  样的事情。比如:子类重写父类的方法,方法上必须有@override标记;若一个方法已过时不用了,就该方法添加注
  解@Deprecated,调用者反射时就明白这方法已过时
  3、注解在哪标记,也就是说能为哪些元素标记?
  可以在包、类、字段、方法、局部变量
  二、自定义注解及其应用
  1、先定义个注解类,如下代码:
/**
* 自定义注解类
* @author Administrator
*
*/
public @interface AnimTest {
}
  2、将这个注解类应用到某个类上,然后用反射查看判断该类是否被这个注解类所标记
package com.itcast.test;
import com.itcast.zhujie.AnimTest;
@AnimTest   //这是自定义的注解
public class ZhujieTest {
/**
* @param args
*/
public static void main(String[] args) {
boolean isAnim  = ZhujieTest.class.isAnnotationPresent(AnimTest.class);
if(isAnim)
System.out.println("it  has one");
else
System.out.println("no have");
}
}
  输出的结果是:no have ;表示该类没有找到注解标记,这是为何呢?不是在类上已经使用了注解了嘛?
  回答这问题之前,我们先学习一个东西,Retention元注释类,指的是注释类型的注释要留多久。如果某个注释类型没有声明Retention元注释,则保留策略为默认的RetentionPolicy.CLASS,表示保留到编译时,运行时就会被剔除。  RetentionPolicy 是一个枚举类型,有三个值:SOURCE、CLASS 、  RUNTIME 分别对应着Java源文件、class文件、内存中的字节码
  我们在重新看下上个代码,AnimTest 没有声明Retention,故保留默认的CLASS,只保留到编译时期。所以内存中加载类文件时,注解类已被清除,所以才会找不到AnimTest注解类的标记。要想在反射中能找到该标记,只要设置下注解类的保留周期,所以接着改下这个注解类
/**
* 自定义注解类
* @author Administrator
*
*/
@Retention(value = RetentionPolicy.RUNTIME)     //表示运行时也保留该注解类
public @interface AnimTest {
}
  在执行代码,结果是:it  has  one
  3、现在有个问题,就是注解类的使用范围是怎样的呢?只能在类上么?
  其实有个元注释Tagert来限定范围的。Tagert 指的是注释类型所适用的程序元素的种类。如果注释类没有声明Tagert元注释,则可以适用于任何元素上,如果声明了,编译器就会强制实施指定的范围
  如果要使AnimTest只作用于方法上,则在注释类声明Tagert
/**
* 自定义注解类
* @author Administrator
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})    //限定范围,作用于方法
public @interface AnimTest {
}
  那么这个注解类就不能作用于类上了,否则会编译异常,只能作用于方法上,代码如下:
public class ZhujieTest {
/**
* @param args
*/
@AnimTest  //作用于方法上
public static void main(String[] args) {
boolean isAnim  = ZhujieTest.class.isAnnotationPresent(AnimTest.class);
if(isAnim)
System.out.println("it  has one");
else
System.out.println("no have");
}
}
  如果注释类既可以使用类上也可以作用 于方法上,只要修改为 @Target(value = {ElementType.METHOD,ElementType.TYPE}),type是表示类型
  三、为注解增加各种属性
  1、现在给注解类添加各种属性,类似接口形式,只提供方法
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.itcast.classdemo.WeekDay;
/**
* 自定义注解类
* @author Administrator
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
public @interface AnimTest {
String color();   //字符串
int count();    //整型
int[] arr();   //数组
MetaAnnotation meta() default @MetaAnnotation("very food");  //注释类型
WeekDay day() default WeekDay.MON;   //枚举类型
}
//注释类
public @interface MetaAnnotation {
String value() default "heollo";    //提供默认值
}
//枚举类
public enum WeekDay {
SUN,MON
}
  2、以下是测试注释类调用属性
import com.itcast.zhujie.AnimTest;
@AnimTest (color ="red",count =1,arr=234)   //这是自定义的注解,并为其属性赋值
public class ZhujieTest {
/**
* @param args
*/
@AnimTest (color ="blue",count=2,arr={3,21})
public static void main(String[] args) {
//是否含有AnimTest注释标记
boolean isAnim  = ZhujieTest.class.isAnnotationPresent(AnimTest.class);
if(isAnim){
//获取AnimTest注释标记对象
AnimTest an = ZhujieTest.class.getAnnotation(AnimTest.class);
//以下都是获取AnimTest注释标记对象的属性
System.out.println(an.color());  // red
System.out.println(an.count());   //1
System.out.println(Arrays.toString(an.arr()));   //[234]
System.out.println(an.meta().value());   //very good
System.out.println(an.day());            //MON
System.out.println("it  has one");
}else
System.out.println("no have");
}
}
  经上述测试总结:
  1)注释类的属性可以有String、int、数组、枚举和注解
  2)注释类的属性可以设置默认值,如String color() default "red";
  3)在使用注释类时,有属性,但没默认值,这时必须要赋值,否则编译不通                过 @AnimTest (color ="blue",count=2,arr={3,21})
  4)获取类上的注释对象,用反射技术AnimTest an = ZhujieTest.class.getAnnotation(AnimTest.class);
  5)获取注释上的属性,类似调用方法一样,需要带括号;
  6)如果注释类只有一个属性要赋值,且属性名为value,则赋值时属性名和等号都可以省略,如:@MetaAnnotation("very food")

posted @ 2014-03-28 11:17 顺其自然EVO 阅读(377) | 评论 (0)编辑 收藏

单元测试覆盖工具coverlipse

 我的工作经常会遇到这样的问题,统计自动化覆盖率,以前做windows的程序可以用ibm的Codecover来实现这种功能,但是对于互联网应用服务器端代码覆盖率的统计一直没有找到好的方法,前阵子听说了coerlipse,今天就预研了一下,虽然没有达到我的目的(无法实现对远程服务器端代码覆盖率的统计,只能完成本地做单元测试的覆盖率统计)。但是还是把学习过程记录一下,说不定以后有用哦。
  首先,需要到http://coverlipse.sourceforge.net/download.php下载插件,该网站提供两种方式,一种是通过eclipse更新插件地址去更新他的插件,一种是将插件先下载过来手工安装。
  手工安装的方法,自然和其他安装插件方法一样,完成如下三步:
  一,解压插件包,关闭eclipse程序。
  二,将插件包中的plugin目录下的文件拷贝到你eclipse安装目录下的plugin目录下;把features下的xml文件拷贝到你elipse安装路径的features目录下。
  三,启动eclipse,点击help->abaout Eclipse platform属性页,可以通过plugin-detail查看你的插件是否被安装成功了。
  现在我们要做单元测试的简单实验了,当然我们首先要新建一个被测工程,一个被测类,一个测试类。在本例子中分别命名如下
  测试工程:Mytest。由于是单元测试工具通常测试工程和被测工程在同一个工程中。根据我对coverlipse的简单研究,似乎也必须在同一个工程内。
  被测试类: helloworld
  被测代码如下:
01  package testp;
02
03  public class helloworld {
04     public String SayHelloWorld(String a)
05     {
06         if (a !="")
07         {
08            return a + " helloworld";
09         }
10         else
11         {
12            return "please input username";
13         }
14     }
15  }
  测试类:testhelloworld
  测试类代码如下:
1   public class testhelloworld extends TestCase {
2      @Test
3      public void testCase1() throws Exception
4      {
5          helloworld hw = new helloworld();
6          String rs = hw.SayHelloWorld("elbert");
7          org.junit.Assert.assertEquals("elbert helloworld", rs);
8      }
9   }
 我们可以通过两种途径来测试覆盖率。一种最简洁的方法,选中junit的测试类,右键点击,选择Run as->w/coverlipse,如下图
  也可以通过在Run dialog上面的设置来设置测试类和被测试类。如下图
  最后我们要查看代码覆盖率了,coverlipse通过两个页签来显示结果
  1.coverlipse marks view:通过这个页签会告诉测试者,被测类中哪些代码行被测试,那些代码行没有被测试。由于例子中的被测类的第12行不会被执行,所以报告结果如下图。
  2.coverlipse class view:通过这个页签可以知道被测类的测试覆盖率。如下图

posted @ 2014-03-28 11:15 顺其自然EVO 阅读(200) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 132 133 134 135 136 137 138 139 140 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜