qileilove

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

谈软件测试流程和追踪

  首先有一个总体端到端流程,实现测试全生命周期管理,即从测试计划,测试方案,测试设计,测试执行,测试评估的全流程管理。这是最高端的一个端到端流程。测试本身可能又分单元测试,集成测试,验收测试几个阶段,每个阶段都应该有一个测试流程贯穿。特别是集成测试和验收测试阶段的时候。

  在这个大流程下面最重要的一个子流程即缺陷管理流程或叫变更管理流程。按标准的工程变更的思路,变更本身又拆分为了变更请求,变更单和变更活动。在缺陷管理中可以使用两层表单对象,特别是大型多小组团队协作,软硬件公有的产品型测试,一个缺陷的发生在CCB分析后都可能涉及到多方的修改和验证,采用两层业务对象模式是最基本的。缺陷提交一般流程为缺陷提交,CCB缺陷分析指派,相关人修改缺陷并提交验证,提交人验证关闭缺陷。在两层业务对象模型下,必须下层对象必须全部关闭后才能给关闭上层。

  其它是一些可选的子流程,相当多,但是对于大型产品型测试往往都需要考虑。

  测试需求提交或测试申请流程,大型产品研发团队中,测试往往是独立运作的部门或团队,为了进行更好的跨团队协同,也为了测试团队的测试工作有相应的输入。需要有相应的测试申请流程,测试申请通过后测试部门才会根据测试申请进行测试。其二,在测试申请流程中,测试团队可以对开发提交的产品版本和相关资料文档进行测试前评估,确认需要测试的内容符合基本的准入要求。

  版本构建流程,在进行测试前需要进行版本构建,开发提交相应的源代码和自动编译脚本,集成人员负责进行版本构建工作,在版本构建完成后配置管理人员进行版本构建审核,开发对构建的版本进行评估,最终确认版本是否构建成功。构建成功的版本则可以朝测试环境发布。大型项目对于构建需要有独立的构建环境来支撑。构建环境包括了自动编译和持续集成,也包括了自动化单元测试,静态代码测试等相关内容。

  版本提取流程,对于构建成功的版本处于待测试状态,在测试总体计划流程中,可以根据测试实际进度提取相应的版本并发布到集成或验收测试环境。同时对于验收测试通过的正式版本,也存在版本提取正式发布到客户现场的需求。

  在各个流程中会涉及到多个基础数据的维护,包括各个角色和用户组,权限的维护。也包括了产品,项目,版本,客户等基础信息的维护。这些信息在流程中作为基础数据使用到。

  下面谈下测试过程中的核心追踪

  首先测试用例是测试设计的一个核心内容,测试用例需要实现条目化管理。测试用例需要追踪到具体的需求用例。在测试和需求间整个追踪链条为系统-》子系统-》功能模块-》需求用例-》测试场景-》测试用例,通过该追踪关系可以看到测试用例对需求的覆盖程度。

  其次缺陷在提交的过程中,必须追踪到对应的测试用例,以分析测试执行对测试设计的覆盖程度。通过需求到测试用例的追踪,测试到缺陷的追踪,可以详细统计到各个功能模块的缺陷数量。也方便后续细粒度的缺陷密度统计。

  对于提交的缺陷,开发在进行修复的过程中往往涉及到源代码的修改,而源代码最终会入配置管理环境。因此可以进一步追踪每个缺陷究竟修改了哪些源代码文件,以方便后续的源代码变更分析和管理。在缺陷提交后,涉及到进行版本构建,版本构建需要确认在当前构建的版本解决了哪些缺陷,哪些缺陷可以进行回归测试和验证。

posted @ 2012-06-26 09:47 顺其自然EVO 阅读(271) | 评论 (0)编辑 收藏

Java中用内存映射处理大文件

  在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如下为一个对比实验。

  1. package test;  
  2. import java.io.BufferedInputStream;  
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.IOException;  
  6. import java.io.RandomAccessFile;  
  7. import java.nio.MappedByteBuffer;  
  8. import java.nio.channels.FileChannel;  
  9. public class Test {  
  10.       
  11.     public static void main(String[] args) {  
  12.         try {  
  13.             FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");  
  14.             int sum=0;  
  15.             int n;  
  16.             long t1=System.currentTimeMillis();  
  17.             try {  
  18.                 while((n=fis.read())>=0){  
  19.                     sum+=n;  
  20.                 }  
  21.             } catch (IOException e) {  
  22.                 // TODO Auto-generated catch block 
  23.                 e.printStackTrace();  
  24.             }  
  25.             long t=System.currentTimeMillis()-t1;  
  26.             System.out.println("sum:"+sum+"  time:"+t);  
  27.         } catch (FileNotFoundException e) {  
  28.             // TODO Auto-generated catch block 
  29.             e.printStackTrace();  
  30.         }  
  31.           
  32.         try {  
  33.             FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");  
  34.             BufferedInputStream bis=new BufferedInputStream(fis);  
  35.             int sum=0;  
  36.             int n;  
  37.             long t1=System.currentTimeMillis();  
  38.             try {  
  39.                 while((n=bis.read())>=0){  
  40.                     sum+=n;  
  41.                 }  
  42.             } catch (IOException e) {  
  43.                 // TODO Auto-generated catch block 
  44.                 e.printStackTrace();  
  45.             }  
  46.             long t=System.currentTimeMillis()-t1;  
  47.             System.out.println("sum:"+sum+"  time:"+t);  
  48.         } catch (FileNotFoundException e) {  
  49.             // TODO Auto-generated catch block 
  50.             e.printStackTrace();  
  51.         }  
  52.           
  53.         MappedByteBuffer buffer=null;  
  54.         try {  
  55.             buffer=new RandomAccessFile("/home/tobacco/test/res.txt","rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 01253244);  
  56.             int sum=0;  
  57.             int n;  
  58.             long t1=System.currentTimeMillis();  
  59.             for(int i=0;i<1253244;i++){  
  60.                 n=0x000000ff&buffer.get(i);  
  61.                 sum+=n;  
  62.             }  
  63.             long t=System.currentTimeMillis()-t1;  
  64.             System.out.println("sum:"+sum+"  time:"+t);  
  65.         } catch (FileNotFoundException e) {  
  66.             // TODO Auto-generated catch block 
  67.             e.printStackTrace();  
  68.         } catch (IOException e) {  
  69.             // TODO Auto-generated catch block 
  70.             e.printStackTrace();  
  71.         }  
  72.     }  
  73. }

  测试文件为一个大小为1253244字节的文件。测试结果:

  1. sum:220152087 time:1464  
  2. sum:220152087 time:72  
  3. sum:220152087 time:25

 说明读数据无误。删去其中的数据处理部分。

  1. package test;  
  2.  
  3. import java.io.BufferedInputStream;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.IOException;  
  7. import java.io.RandomAccessFile;  
  8. import java.nio.MappedByteBuffer;  
  9. import java.nio.channels.FileChannel;  
  10.  
  11. public class Test {  
  12.  
  13.       
  14.     public static void main(String[] args) {  
  15.         try {  
  16.             FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");  
  17.             int sum=0;  
  18.             int n;  
  19.             long t1=System.currentTimeMillis();  
  20.             try {  
  21.                 while((n=fis.read())>=0){  
  22.                     //sum+=n;  
  23.                 }  
  24.             } catch (IOException e) {  
  25.                 // TODO Auto-generated catch block  
  26.                 e.printStackTrace();  
  27.             }  
  28.             long t=System.currentTimeMillis()-t1;  
  29.             System.out.println("sum:"+sum+"  time:"+t);  
  30.         } catch (FileNotFoundException e) {  
  31.             // TODO Auto-generated catch block  
  32.             e.printStackTrace();  
  33.         }  
  34.           
  35.         try {  
  36.             FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");  
  37.             BufferedInputStream bis=new BufferedInputStream(fis);  
  38.             int sum=0;  
  39.             int n;  
  40.             long t1=System.currentTimeMillis();  
  41.             try {  
  42.                 while((n=bis.read())>=0){  
  43.                     //sum+=n;  
  44.                 }  
  45.             } catch (IOException e) {  
  46.                 // TODO Auto-generated catch block  
  47.                 e.printStackTrace();  
  48.             }  
  49.             long t=System.currentTimeMillis()-t1;  
  50.             System.out.println("sum:"+sum+"  time:"+t);  
  51.         } catch (FileNotFoundException e) {  
  52.             // TODO Auto-generated catch block  
  53.             e.printStackTrace();  
  54.         }  
  55.           
  56.         MappedByteBuffer buffer=null;  
  57.         try {  
  58.             buffer=new RandomAccessFile("/home/tobacco/test/res.txt","rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 01253244);  
  59.             int sum=0;  
  60.             int n;  
  61.             long t1=System.currentTimeMillis();  
  62.             for(int i=0;i<1253244;i++){  
  63.                 //n=0x000000ff&buffer.get(i);  
  64.                 //sum+=n;  
  65.             }  
  66.             long t=System.currentTimeMillis()-t1;  
  67.             System.out.println("sum:"+sum+"  time:"+t);  
  68.         } catch (FileNotFoundException e) {  
  69.             // TODO Auto-generated catch block  
  70.             e.printStackTrace();  
  71.         } catch (IOException e) {  
  72.             // TODO Auto-generated catch block  
  73.             e.printStackTrace();  
  74.         }  
  75.  
  76.     }  
  77.  
  78. }

  测试结果:

  1. sum:0 time:1458  
  2. sum:0 time:67  
  3. sum:0 time:8

  由此可见,将文件部分或者全部映射到内存后进行读写,速度将提高很多。

  这是因为内存映射文件首先将外存上的文件映射到内存中的一块连续区域,被当成一个字节数组进行处理,读写操作直接对内存进行操作,而后再将内存区域重新映射到外存文件,这就节省了中间频繁的对外存进行读写的时间,大大降低了读写时间



posted @ 2012-06-25 09:45 顺其自然EVO 阅读(244) | 评论 (0)编辑 收藏

如何将数据库从MySQL移植MemSQL

  本例子假设你的MySQL和MemSQL都跑在同一台机器上,MySQL 在3306 端口,而MemSQL在 3307端口,同时假设二者都可以通过root账号无需密码访问。

  使用mysqldump工具移植

  mysqldump是MySQL客户端最常用的数据备份工具之一,它会生成一些列创建表和插入数据的SQL语句,因此用来恢复一个数据库是最方便的。

  当你确定要将数据迁移到MemSQL之前,有几个注意事项:

  大多数MySQL存储引擎都是使用 B-tree 来存储索引的,而 MemSQL 是使用单向无锁的 skip 列表或者无锁的哈希表。选择正确的索引数据结构对应用程序的性能会有显著的提升。其中哈希表主要适合 key-value 的查找,而 skip 列表特别适合用于复杂范围的扫描和排序(ORDER BY)。因此在进行迁移之前,你得重新审视你得表定义并确定是否能使用 MemSQL 专有的优化。默认的 BTREE 符号将被转成升序的 skip 列表。如果你需要对某个列做双向的范围扫描,你可以考虑同时增加升序和降序的索引,更多 MemSQL 的索引信息请看 indexes

  因为 code generation 的缘故,  MemSQL 首次加载数据库结构的速度要比 MySQL 慢,因为 MemSQL 首次加载表结构时,会生成并编译代码来实现这个表的架构,包括内存分配、插入、删除和迭代方法等等。一旦表被编译完成,MemSQL 将在整个运行期间直接使用编译好的代码。而 mysqldump 生成的 INSERT 语句也将特别编译一次。

  mysqldump 会生成一些 MemSQL 不支持的 SQL 语句,例如不支持 UNIQUE_CHECKS,为了更好的支持 mysqldump,MemSQL 对这些不支持的语句只是予以警告,可通过调整warn_level 变量来控制错误的级别,详情请看 Unsupported Features . 某些 CREATE TABLE 语句可能被完全阻止执行,如果你遇见这种问题,只能手工去修改表的定义SQL。

  如果你运行 MemSQL 的机器没有足够的内存来加载数据,服务器将会对 INSERT 语句执行报 out-of-memory 的错误信息,这种情况你只能安装更多的内存。如果你将已有的 memsqlbin 目录复制到新的机器上,那么MemSQL 会继续沿用已编译的表定义和 INSERT 语句。如果你使用的是 MemSQL 开发版,而且超过了10G 的限制,那你可以参考 memsql.com/next 来了解如何升级。

  建议你将数据库结构定义和数据分开独立文件存放,这样就可以在需要的时候来调整表结构,你可以使用下面命令来分别导出数据库结构和数据文件:

$ mysqldump -h 127.0.0.1 -u root -B [database name] --no-data > schema.sql$ mysqldump -h 127.0.0.1 -u root -B [database name] --no-create-info > data.sql

  然后通过下面方法导入:

$ mysql -h 127.0.0.1 -u root -P 3307 < schema.sql$ mysql -h 127.0.0.1 -u root -P 3307 < data.sql

  运行这些步骤时,你可以观察 memsql_tracelog(同时也输出到 stderr)来查看执行过程中被忽略的不被支持的特性。一旦导入完成后,你可以连接到 MemSQL 并查询结果。

  转换你的应用程序

  让应用程序支持 MemSQL 非常简单,只需要改连接配置即可。

  注意

  如果你要连接到本机的 MemSQL 应该使用 127.0.0.1 而不是 localhost,多数 MySQL 客户端可将 localhost 解析并使用 MySQL 的 socket 文件进行连接而忽略端口设置,详情请看 this page

  一旦你开始运行程序,可监控 memsql_tracelog。一些不支持的SQL语法会在这里显示。

posted @ 2012-06-25 09:41 顺其自然EVO 阅读(346) | 评论 (0)编辑 收藏

软件开发为何采用持续集成

  在软件开发过程中,我们会涉及到配置管理、源码控制、发布计划、审计、符合性和集成,以及构建测试和部署流程、验收测试、依赖管理和生产环境的创建与管理,很多人认为这些与确定需求、实现需求、写代码相比,这些活动并不那么重要,它只为是软件开发过程很小的一部分并且不需求多大的技术投入。其实不然,恰恰相反它们会消耗大量的时间和精力,而且是成功交付软件的关键因素。假如没有关注这一方面带来的潜在风险,就可能耗费大量不必要的资金,甚至导致整个项目严重延期。

  如果没有持续集成,我们可能会遇到下列问题:

  1、如何将一个想法以最快的速度交付给用户。

  2、如何协调开发人员、测试人员,以及构建和运维人员在一起高效地工作

  3、在发布当天是否遇到很多的问题,发布过程时候需要很长的时间。

  4、是否在开发完成之后才发布软件,失败后长期的加班。

  5、如何快速的获取用户的反馈,并让团队人员及时的改正。

  6、生产环境是否还是采用的手工部署,还在为不同环境之间的部署烦恼。

  7、在开发过程中,应用程序在相当长的一段时间内无法运行,无法验证每次修改的正确性。

  一行代码的改动需要花多长时间才能部署上线,处理方式是否可重复且可靠呢?从决定做某种修改到该修改结果正式上线的这段时间称为周期时间。对任何项目而言,它都是一个极为重要的度量标准。

  软件开发的首要任务是尽早交付有价值的软件并让客户满意,速度是至关重要的,因为未交付的软件就意味着成本。如果不能按时按量的交付,就会产生不必要的成本浪费。而为了更好的协调组内人员高效的工作,在整个交付过程中,团队中的每一个成员都必须对每一个交付特性负责,无论什么样的修改都应该触发反馈流程,反馈应该尽快发出,团队必须接受反馈,并依据它作出相应的行动。快速的交付使你能够验证那些新开发的特性或者修复的缺陷是否真的有用。

  为了达到短周期、高质量的交付目标,发布软件必须自动且频繁化。

  自动化:构建、部署、测试、发布

  频繁化:频繁发布,每个发布版本之间的差异会很小,大大减少与发布相关的风险,且容易回滚,频繁发布也会加快反馈速度。每当有人提交代码,就对整个应用进行构建,执行全面的自动化测试,以验证其正确性。更重要的是,如果构建或测试失败了,开发团队就要停下手头的工作,立即修复它,时时刻刻都要让正在开发的软件一直处于可工作的状态。

  实现持续集成,你需要的:

  1、版本控制,所有涉及产品的都要加入到版本控制中,包括构建与部署脚本。

  2、自动化构建,自动化的构建整个过程,缩短周期时间(Cycle time)。

  3、持续集成系统,Jenkins, ThoughtWorks公司的Go,JeBrains的TeamCity等。

  提交代码必须遵守以下原则:

  1、查看构建是否成功,如果失败,停下手头的工作和团队中的其他人一起修复。

  2、如果构建且测试全部通过,就从版本控制库中的代码更新到自己的开发环境上。

  3、在自己的修改上重新构建,运行所有的测试,确保在自己机器上的代码都是正确的且工作正常。

  4、如果本地构建成功, 再从版本构建库中更新代码到本地, 再做一次构建,如果成功的话,就提交本地的修改到版本库中。

  5、在远程持续集成服务器上再次构建包含你的这次提交的构建结果(通常是自动的)。

  6、如果构建失败,就停下手中的事,在自己的开发机上修复这个问题后,再重新回到步骤3.。

  7、构建成功,小庆祝一下,并开始下一项任务。

  持续集成缩短从想法到商业价值实现的时间,减少时间和降低风险,增加反馈,改进负责交付的开发、测试和运维人员之间的协作关系。

posted @ 2012-06-25 09:39 顺其自然EVO 阅读(271) | 评论 (0)编辑 收藏

我的自动化软件测试小结(2)

 世上本没有路,走的人多了,也就有了路。今天不讲技术,只讲感悟。

  第一节:质量意识是自动化的前提

  任何东西都有个价格,质量也一样,如果在产品的战略定位中,质量不是核心价值,那么质量的价格自然不会高,为质量付出的代价也不会高。

  普遍讲,客户端产品的质量要求高于Web产品,客户端出了Bug,用户修复很麻烦,Web产品出了Bug,可以临时上线,或在下次上线修复;Web产品中,涉及money的质量要求高于没有盈利模式的产品,比如:游戏 or 购物类;还有就是用户群体的大小和产品活跃度,等等。

  第二节:减轻测试负担是现实动机

  古语有云:凡是机器可以解决的,就把它自动化吧!

  有代码重构和服务器配置变更等涉及面广大的任务时,须要大量的回归测试,这时候先使用接口测试回归,可以速度得到反馈,确保功能无错,再配合UI测试。

  日常工作中,不会天天重构,但做一下每日回归,上线前再密集的多回归几个轮次,应该是比较靠谱的。

  自动化脚本尤其是接口测试脚本还可以用于批量准备测试数据,偶尔用用,不亦乐乎。

  第三节:结合产品本身的状况是必由之路

  额。。。这里的水太深了。简单讲,自动化不贴合一个产品本身的特点,十有八九是举步维艰。

  一个比较理想状况是,产品的发展思路比较清晰,项目流程比较规范,上线的节奏稳定有序。在这种情况下,测试比较容易和开发沟通,取得开发的协助写用例,也比较容易确定啥时候写用例,跑用例,维护用例,维护的用例也可以反复利用,而不至于因为产品的频繁变化而失效,反而拖累大家陷入维护用例的泥沼。

  第四节:持续投入,方可见到效果

  无论从哪个角度看,自动化发现的Bug都是很少的,自动化主要是用来回归的。

  用例只有十几二十个,不会有太大的收益,没有量的积累不会有质的变化。写很多用例?很大的成本,而且,产品在不断发展,用例本身也要不断维护,这些都是成本,看起来是个无底洞。

  一天跑个一次两个,搞个每日回归,收益不见得高,最好是搞成持续集成,密集的连续跑。但是,用例挂了谁来检验?谁来维护?谁来反馈?如何做到及时?这些都须要人力。

  所以,做自动化,不做则已,一做就要有花成本的勇气,用例要足够,至少覆盖所有核心的功能点,尽量做到持续集成,毕竟都是敏捷开发模式,代码提交的频度很高,还要有坚持扩充,维护用例库的恒心。

  一个优良的自动化框架的价值主要体现在这里:使得用例好写,好维护,运行稳定,运行快。

  发现Bug不是我们的目的,保证质量才是。

posted @ 2012-06-25 09:37 顺其自然EVO 阅读(269) | 评论 (0)编辑 收藏

银行系统性能测试策略探讨

  2、负载目标计算

  与项目管理中的SMART原则类似,业务场景需转换成可量化、可衡量、可实现的负载目标才能进行性能测试,而负载目标要根据不同场景分别计算。根据上阶段收集到“原始”数据,本阶段可计算或指定出各种间接和直接的负载目标值,一般负载目标多从两种角度考虑:

  2.1 前端角度

  在线用户数量:间接负载目标值,可理解为所有能操作被测交易的用户(柜员)数量。

  平均操作时间(思考时间):间接负载目标值,与在线用户数量一同计算业务并发数量。

  业务并发数量:典型场景中集中操作(不是绝对并发)交易的用户(柜员)数量。

  2.2 后端角度

  每秒交易数量(TPS):根据日志计算出的被测系统应承受的每秒交易数量。

  交易响应时间(TRT):与TPS对应,负载场景下交易应达到的响应时间。

  吞度量(Throughout):LoadRunner中吞度量是以每秒收到的字节数计算,其实TPS也是吞吐量的一种,根据不同被测系统计算对应的吞吐量。

  系统并发数量:区别于业务并发数量,系统并发数量更趋近于绝对并发。对长连接系统来说系统并发就等于允许的连接数,对短连接系统来说更多取决于架构。

  被测系统CPU、Mem、Disk等指标值:多为指定值,如CPU利用率不高于80%等。

  前端负载目标需要通过大量、广泛的业务和日志统计才能得出,如需记录下高峰时段操作交易的用户数量、估算用户状态比例、统计操作习惯等,由于考虑到人的因素,因此前端负载很难计算精确。前端负载目标适合交易关联不大、操作用户分散、无具体业务量要求的系统,如内控管理、培训考核、网银主页等(以B/S架构为主)。

  后端负载目标则比较容易计算,如当前某省对公汇兑类交易日均8万笔,则TPS=80000/(6*60*60)=3.7笔/秒(对公按6小时计算);核心系统联机交易平均响应时间在3秒以下等。后端负载目标适合交易关联度大、操作用户相对集中、有具体业务量要求的系统,以银行核心业务系统为主。

  负责目标需要针对不同类型的被测系统计算合理的目标值,同时还需考虑2/8原则,未来业务量、用户量扩展等因素,这里不做赘述。  一、性能测试的四个方面

  在一般的性能测试讨论中大家通常只围绕三个方面进行提问和总结:测试脚本如何编写,被测系统如何监控,性能瓶颈如何调优。大部分刚刚接触性能测试的人会纠结于脚本的编写,如何设置参数化、如何设置关联、何时插入检查点,各种论坛和讨论群里不断有人提出也不断有人解答。经过一段时间的经验积累后就会遇到如何监控被测系统,如何确定瓶颈并调优等问题,各种操作系统、中间件、数据库的监控和调整方法也被口耳相传。但我认为在性能测试的讨论中我们却忽略了另一个重要的方面:如何制定性能测试策略

  我认为完整的性能测试可以分为四个方面:1、测试策略制定;2、性能脚本编写;3、被测系统监控4、性能瓶颈调优。从流程来看这四个方面有先后顺序的关系,从领域来看这四个方面都有可深入研究的内容。

  1、测试策略决定了性能测试如何开展,负载目标是否正确,场景模拟是否合理,好比战场上的指挥部,从全局设计战争的方向。

  2、测试脚本编写决定了性能测试能否顺利执行,压力发起是否正确,好比战场上直面敌人的作战单位,需要真刀真枪的去拼杀。

  3、测试监控决定了关注范围是否合理,能否发现瓶颈所在,好比潜入敌后的地下组织,要设置在关键的脉络上实时收集情况。

  4、性能调优并不是所有项目都需要的,但一旦发现问题,调优就决定了测试能否完美收官,如战场上的特种兵,用专业的技能解决各种难题,非一时所能练成,所以我们大部分人都执著于此。

  本文以笔者实际项目经验为基础,尝试探讨并总结银行系统性能测试策略制定过程中必经的步骤和所需考虑的内容。

  二、测试需求分析

  1、业务场景分析

  测试银行核心系统时将柜员签到、签退、业务操作、批量等众多交易放在同一场景中执行,这样的场景在现实中是否存在?测试POS、ATM等渠道时将查询、动账类交易各按50%的比例分配,这样的比例是否正确?所有的性能测试都是以复现实际业务场景为目标。因此业务场景分析和选取务必严谨。业务场景应该从时间和空间两个角度考虑:

  1.1 时间角度

  分别以一年、一月、一天的角度观察,被测系统是否存在业务高峰时段。各高峰时段的重点交易是什么,交易比例如何。

  1.2 空间角度

  根据各分行业务特点不同,被测系统是否存在不同的高峰场景和交易,操作交易的柜员数量及一般操作时间是多少。

  业务场景分析阶段应向业务、研发、运维等多部门了解被测系统需求,但就数据准确度而言,应多参考生产日志。对升级改造类系统,场景分析的数据可从老系统的生产日志中获取;对新开发的系统,分析数据可从业务上有关联的其他系统中获取,同时参考业务部门意见。典型业务场景可以不止一个,但一定要明确各场景中的交易和比例,不要模拟一个不存在的大混合!

  三、测试环境设计

  1、硬件环境设计

  大部分性能测试的硬件环境与生产相去甚远,受品牌、型号、部署方式(集群个数减少,软负载均衡代替硬负载均衡)等多种因素限制,无法直接将测试环境下的性能结果向生产环境做比例放大,因此硬件环境设计主要关注应用架构与生产环境一致(如应用、数据库服务器必须为集群方式),尽量减少性能测试中的硬件资源瓶颈(如数据库存储必须为SAN,发压与被测系统间网络带宽必须为1000M)。

  2、软件环境设计

  软件测试环境可从两个方面考虑:

  2.1 基础配置

  确认操作系统、中间件、数据库和被测系统的版本与补丁与生产环境一致,但操作系统、中间件、数据库的内部参数应根据测试硬件环境做适当调整,可参考生产中的设置比例。

  数据库分区、索引等必须与生产或预计投产时一致。

  2.2 外联系统

  尽量减少外联系统,因为外联系统越多,测试链条越长,环境越难维护,问题越难定位。可适当在被测系统中做挡板程序,模拟与外联系统的应答,但必须保证被测系统中的逻辑分支没有被屏蔽。

  3、铺底数据策略

  性能铺底数据量的大小和分布情况会对测试结果产生极大影响,是场景设计阶段的重中之重!测试前对铺底数据的构造可从以下四个方面考虑:

  3.1 表分区情况

  确认测试环境与生产环境(或预计投产后)的表分区和索引分区规划一致,结合表清理策略确认典型场景中各分区、各表中的数据存量大小和分布情况。

  3.2 表清理策略

  确认生产环境(或预计投产后)的数据库表(尤其业务热表)清理和备份策略,与表分区情况配合,预铺数据并确保测试数据库表中数据存量与生产保持同一量级。

  3.3 表维护策略

  确认当前生产数据库表(尤其热表)和索引统计值更新、rebuild和reorg等操作的频度,在数据预铺中适当更新统计值,切勿在测试环境中频繁执行统计值更新等操作,造成虚假的性能表现。

  如下图,更新统计值后表和索引的相关信息会统计正确,性能将得到一定程度的提升。

  3.4 合理的业务数据

  确认铺底数据中的柜员、行部、角色、权限、待处理任务等业务数据是否符合实际情况,切勿为图省事而创建超级行部、全能柜员和过量的待处理任务。



  四、测试场景设计

  1、发压数据策略

  测试发压数据应在数据铺底时一同考虑,但发压数据需要和压力工具配合使用,可从以下四个方面考虑:

  1.1 表分区情况

  确认发压数据覆盖各表分区,且在分区中也需尽量离散,不要集中操作(主要是查询)同一范围内的数据。

  1.2 表维护策略

  通常生产环境中数据库表在日终结束后执行统计值更新,对应第二天营业时的数据状态,除柜员签到外,其余典型场景均发生在更靠后的营业时段,因此不要在刚刚执行完统计值更新的环境中执行正式的性能测试,建议再执行一段时间的数据预铺,模拟行部营业一段时间后的业务,这时再执行正式的性能测试,并记录结果。

  1.3 被测交易分支

  在能力允许的情况下应尽量多的阅读被测交易源码,或向开发人员了解程序逻辑和交易分支,确认发压数据可覆盖程序中所有重点路径。避免造成该复现的问题没有复现,该出现的瓶颈没有出现。

  1.4 合理的业务数据

  与发压工具配合,确认在测试过程中没有同一柜员并发操作交易,没有超出合理范围的待处理任务,以日峰TPS为负载目标时不要同时执行疲劳测试,这样会导致交易量远远大于实际。

  2、测试并发设计

  并发设计与负载目标紧密关联,同样可分为前端、后端两种方式:

  2.1 前端角度

  使用工具模拟的并发数量代表业务并发用户数量,一个并发进程或线程即模拟一个操作交易的柜员,脚本中设置thinktime模拟平均操作时间,维持典型场景中各交易的用户比例,并做等比例递增,关注此时被测系统的处理能力。但需注意的是,受thinktime和响应时间等影响,业务并发数量≠系统并发数量,所以不能在结论中说“被测系统只能承受XX并发”。且业务并发数量与在线用户数量的换算不可能完全精确,所以也不能用类似10个业务并发即代表100个在线用户的粗略换算为结论。

  2.2 后端角度

  使用工具模拟的并发数量不代表用户数量,也不完全等价于系统并发数量,它仅通过并发的形式对被测系统产生压力。由于后端角度更关注TPS,因此并发递增时各交易之间的并发比例可能会变化,响应时间变慢的交易会增加更多并发以维持预期TPS。

  后端角度重点关注并发增加时的TPS和响应时间变化趋势,确定被测系统的处理能力。

  五、测试策略维护

  测试策略应在测试执行前确定,执行过程中不做大的变更,但仍需根据测试结果不断反思和维护策略,确保性能测试能够真实复现生产场景。

  六、总结

  工欲善其事必先利其器,在性能测试中我们的武器不仅仅是发压和调优工具,还有测试的思想!性能测试不是一个流水化作业的过程,它必须深入被测领域,对业务和技术进行全面了解才能正确执行。本文探讨并总结了银行系统性能测试策略的制定,由于经验有限,难免会有偏颇和遗漏,请批判阅读,同时对其他领域的策略制定也是抛砖引玉。

版权声明:本文出自 dionysus 的51Testing软件测试博客:http://www.51testing.com/?5939

原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。

posted @ 2012-06-25 09:18 顺其自然EVO 阅读(1794) | 评论 (1)编辑 收藏

探索式测试:基于测程的软件测试管理

探索式测试:基于测程的软件测试管理

  为了有效地管理测试,测试领导需要评估测试团队的生存力、当前测试的进度、测试覆盖的范围、已经暴露的风险、测试人员是否需要帮助等因素。一个好的测试流程可以帮助测试领导和测试团队了解这些因素,并实施积极的管理。为了使探索式测试满足软件开发团队对可管理性的要求,Jonathan Bach和James Bach提出了基于测程的测试管理(Session-Based Test Management,简称SBTM)[Bach2000]。本文将介绍SBTM的概念与方法。

  Session的翻译:测程

  在翻译Session时,我遇到了困难,因为现有的中文表达难以传递出SBTM中Session的两层含义:

  ● Session是一段不受打扰的测试时间(通常是90分钟),是测试管理的最小单元。

  ● 一系列Session相互支持,有机地组合在一起,周密地测试了整个产品。

  在如此语境下,Session的同义词是Term(学期、会期)、Period(时段、课时)、Semester(学期),但是直接使用这些同义词的中译并不适当。进过反复考虑,我将Session翻译为“测程”,原因有三点:

  ● 用专用术语“测程”指代SBTM的Session,与Session的其他含义或中译(如“会话”)做明显的区分,这有利于快速、清晰地传达语义。

  ● “测程”表达出Session的基本语义:一段专注于测试的过程。

  ● “测程”与“课程”有相似的词汇结构,暗示了一系列测程组合在一起研究了整个产品,正如课程通过一系列课时讨论了一个完整的领域。

  测程的四个要点

  测试专家Michael Bolton用一页幻灯片总结了SBTM的特征与内容[Bolton2006]。

  如幻灯片的标题和右侧的图画所示,SBTM的重要特征是将测试过程分解为一组测程,从而提高整个测试项目的可说明性(Accountability)。为此,一个测程包含四个要点:主题(Charter)、时间盒(Time Box)、可评审的结果(Reviewable Results)和简报(Debriefing)[Bach2004]。

  主题(Charter)是一个测程需要完成的任务。该任务应该是清晰且具体的,可以在90分钟的时间内完成,并提供有价值的简报。主题通常用一段简练的文字描述,其内容可以是测试一个功能、检查一个风险、测试一组用户情景(User Scenario)等。以下是两个实例[Michael2007]:

  ● Create a test coverage outline and risk list for DecideRight. (为产品DecideRight生成测试覆盖大纲和风险列表)

  ● Explore a decision created with QuickBuild – the wizard that guides the user through the options, criteria, and weights needed to calculate the best decision. (探索QuickBuild如何产生一个决策——用户向导应该引导用户使用选项、标准和权重来计算最佳决策)

  时间盒(Time Box)是一段不受打扰的测试时间,其长度一般在60~120分钟,以90分钟较为常见。在此期间,测试人员不回答问题、不回复邮件、不应答即时消息,只专注地实施测试。从工程师的角度,时间盒使测试人员能排除干扰,全力应对测试的智力挑战。从测试团队的角度,固定且专属的时间盒使得测试规划和进度追踪变得更容易。

  可评审的结果(Reviewable Results)是测程的产出,常见的形式是测程表(Session Sheet),其内容可以包括:

  ● 主题(Charter)
  ● 测试人员
  ● 测试所覆盖的区域
  ● 测试设计和测试发现
  ● 测试所发现的缺陷(Bugs)
  ● 测试所发现的问题(Issues)
  ● 测试所使用的数据文件
  ● 测试活动的时间统计:在产品安装、测试设计与执行、缺陷调查与报告、非测试活动中各花费了多少时间。

  简报(Debriefing)通过面对面的交流将测试情况传递给测试领导。在一天的测程结束后,测试人员向测试领导当面汇报测试情况。领导查看测程表,提出一些问题,测试人员解释测试结果,并回答疑问。测试领导也可以召开小组会议,让测试人员轮流报告当天的测试结果,使测试团队对当前产品的质量形成较完整的认识。

  测程表(Session Sheet)

  传统上,测程表是一个文本文件,拥有固定的格式。这样,测试领导可以用一个文本处理程序从多个测程表中提取信息,形成聚合的测试报告。测试人员也可以用程序读取测程表,将其中的缺陷记录自动提交到缺陷管理系统。Michael Bolton曾经分享过两个测程表的实例[Bolton2007],本文摘录一个实例如下。

  该实例反映了测程表的几个特点:

  ● 测程表记录了一些任务分解(Task Breakdown)数据,如在测试设计和执行(Test Design and Execution)、缺陷调查和汇报(Bug Investigation and Reporting)、测程建立(Session Setup)等活动上花费的时间。这些数据配合简报有助于测试领导估算测试速度、评估测试效率。

  ● 测程表拥有固定的格式,该实例用特殊符号“#”标记了文本处理程序可以提取的数据。测试人员只要遵循简单的格式,就可以产生易于自动分析的测程表。一个简单的文本处理程序(或脚本)能够批量地处理测程表,产生测试报告和图表,并完成自动化任务(如提交缺陷记录到缺陷管理系统)。

  ● 测程表列举了测试所使用的数据文件(Data Files),为测试数据复用提供了基础。

  ● 测程表的核心是测试笔记(Test Notes),它简略地叙述了测试故事(Testing Story):为什么测试,如何测试,为什么认为我们的测试是足够好的。

  ● 测程表记录了缺陷(Bugs)和问题(Issues),它们不但是测程的直接产出,还是规划未来测试的参考资料。

 近年来,出现了更多的SBTM支持工具[Carvalho2011],能够支持更富表现力的测程表。例如,RapidReporter可以方便的生成CSV和HTML格式的测程表,CSV格式便于进一步地自动处理,HTML格式则支持较复杂的排版和屏幕截图。

  聚合测程表收集的数据,测试领导可以评估团队的测试速度。下图显示了已执行测程的总时间随日期的变化趋势,这有助于测试领导评估在项目结束前还可以执行多少测程[Jonathon2004]。如果余下的测试时间不足以完成测试使命,他需要采取措施,以避免项目失败。

  SBTM是动态管理

  在项目之初,测试团队对产品还不够了解,测试领导可以安排一些“侦查型”的测程去学习产品的各个区域。例如,上文提到的“为产品DecideRight生成测试覆盖大纲和风险列表”就侦查了产品的主要功能和风险。基于这些测程的测程表和简报,测试领导可以拟定测试项目的总体计划,并大致规划出测程的数目与主题。

  在项目过程中,好的测试领导会通过测程表和每日简报,积极发现产品或测试的问题,实施有针对性的解决方案。例如,测试领导老王通过阅读测程表,发现测试人员小张常常花费过多的时间在缺陷调查上,他会与小张交谈以了解情况。面对面的交流使信息得到高效地传递,并有助于消除统计数据的误导和书面文字的歧义。如果小张是喜欢打破沙锅、追根究底,老王会告诉他:在调查缺陷时,可以设定一个最长用时(如15分钟),当时间用尽,应该停止调查,根据已知情况提交缺陷报告。此外,他还会安排一些有技术挑战的任务给小张,以帮助他的技术成长。如果小张是不了解产品而花费了过多的调查时间,老王会安排有经验的员工指导小张,或亲自传授一些知识和技能,以帮助他渡过难关。如果是糟糕的可测试性导致了过长的调查时间,老王会和开发团队联系,提出改进意见,以推动产品可测性的提高。

  随着产品和项目环境的变化,测试内容与策略也要做相应的调整。测试领导会根据测程的结果来调整下一步的测试计划。他可以新增几个测程,以调查最新发现的风险;他也可以合并几个测程,将省下的时间分配给更重要的测程。一切调整的目标都是为了优化测试的价值。

  由以上论述不难看出,SBTM与敏捷开发宣言[Agile01]是高度一致的。在原则上,他们都认同:

  ● 个体和互动高于流程和工具

  ● 响应变化高于遵循计划

  在实践上,他们都认可:

  ● 围绕被激励起来的个人来构建项目。提供他们所需的环境和支持,并且信任他们能够完成工作。

  ● 在团队内部,最有效率与效果的传递信息的方法,就是面对面的交流。

  可见,SBTM和敏捷开发虽然来自于不同的专家、实践和社区,但是他们拥有相似的核心,其思想和方法可以相互借鉴与补充。

  SBTM是管理框架

  测试专家Paul Carvalho指出SBTM一个管理框架(Management Framework),其基本元素是:设定清晰的主题、固定长度且不受打扰的工作时间、产生可检查的结果、利用评审和简报来驱动未来的Session [Carvalho2010]。该框架的合理名词也许不是Session-Based Test Management,而是Session-Based Task Management。个人或团队可以用它管理各种类型的(创造性)活动,如编程、写作、锻炼身体、整理房间等。

  从这个角度,SBTM与SMART 原则(Specific, Measurable, Achievable, Relevant, Time-boxed)和番茄工作法有异曲同工之妙。

  ● Specific(具体的):一个测程需要一个具体的主题,一个番茄钟需要一个具体的目标。

  ● Measurable(可度量的):测程产出测程表以反映测试进展。番茄钟关注当前任务是否完成,并收集过程指标。

  ● Achievable(可实现的):对于测程和番茄钟,主题和目标应该是可实现的。这潜在地要求将一个大目标分解为多个小目标,且每个小目标也是具体的、可度量的、可实现的。而且,追踪小目标的完成情况提供了整体进度的可度量性。

  ● Relevant(相关的):测程要为测试项目服务,要切合当前情况,并优化产品价值。番茄钟要针对最重要的任务,以实现自我承诺。

  ● Time-box(有时间限制的):测程和番茄钟都提供了一个固定的时间段以一心一意的工作。

版权声明:本文出自 liangshi 的51Testing软件测试博客:http://www.51testing.com/?298785

原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。


posted @ 2012-06-21 10:32 顺其自然EVO 阅读(282) | 评论 (0)编辑 收藏

学习TDD:TDD的好处

学习TDD:TDD的好处

TDD的全称是Test Driver Development,测试驱动开发。就是开发要以测试为驱动。编码之前,测试先行。代码都没有,我如何测试,我连要测的对象都没有啊?这好像是个问题。

  TDD的哲学为我们解答了这个问题:先编写测试用例(没有代码之前这些测试用例一个也通不过),然后再写代码让这些测试用例通过。更进一层的讲就是:编写足够的测试用例使测试失败,编写足够的代码是测试成功。我们编码的目的更加明确的。

  TDD是大名鼎鼎的极限编程的一个最重要的设计工具之一(另一个是重构(Refactoring),这是它的荣誉,下面我列举几点它实际的好处。

  TDD带来的好处有:

  1、你会更加站在用户的角度去看你将要完成的产品,你要尽可能想到用户所有进行的操作。而不是从程序员的角度想用户应该会如何去使用我们的产品。

  2、测试用例是在对功能进行测试。在写代码之前先写测试用例,可以对我们编写代码提供指导性的参考。防止我们漏掉一些功能。

  3、它使我们对自己代码有了信心,因为我们事先设计好的所有测试用例都Pass了。

  4、如果在更改代码后测试用例不能通过,我们可以根据不能通过的测试用例精确的定位问题,并轻而易举的解决的这个bug

  5、哈!我们的一整套完备的测试用例在这里替我们把关(把的什么关?),我们就可以十分安全的使用极限编程的另一个最重要的工具——重构。重构改变的是代码的内部结构,而不会改变外部接口功能。知道在做重构时测试用例是把的什么关了吧!很明显,测试用例是保证我们在进行重构时,不会影响到代码的外部接口功能。所以我刚刚说,我们进行的重构是十分安全的。

  6、基于第5点,我们找到了重构的信心,必要时候你还可以痛痛快快的并且满怀信心的对代码做一场大的变革。这样我们的代码变得干净了,扩展性、可以维护性以及易理解性纷至沓来。

  TDD有这么多好处,但它也不是免费的午餐,它需要我们有设计完备的测试用例的能力(这项能力是长期理论与实践的结合体),否则你将会吃了亏编写了一大堆测试用例,却没测到点子上。可怕的是,你还对你“测试通过”的糟糕的代码满怀信心。

  TDD的主旨是高效,可能这个高效不是非常高的开发速度。

  通常的软件开发过程是先写功能,然后再写测试。甚至有时候只进行某些方面的测试,或者有省事的就略去了某些细小功能的测试,或者是忘了对这些模块的测试。

  这些看起来对软件影响不大,但是似乎有点过于自信了。因为我们是人,不是神,所以难免会出一些这样或那样的错误,而这些小的问题在项目整合起来以后进行排错是非常令人头疼的。

  TDD的思想是以测试推动开发进程。因为我们在软件开发之前,每个程序单元的功能都已经确定了。程序员在理解完整个程序需求以后,直接进行开发,有可能会因为种种原因考虑不很周全,似乎功能实现的没有问题了,但是其中却可能隐藏着非常可怕的Bug。TDD促使开发人员先根据程序单元的功能编写测试代码,就像是先建一个模型,然后向里面浇注合适功能的代码。最后满足所有的测试验证了,才能正常通过测试,这个程序单元才算完成。

  这样消除了开发人员主观性的对程序单元健壮性的评估,更客观的验证每一个程序单元的功能实现以及可能出现的Bug。

  当然,这些操作都需要有大量的代码支持,所以费事是在所难免的,但是这点“费事”与健壮性非常强的代码相比,有些人还是偏向于使用TDD。

  首先站在客户方代码的立场,可以获得更好的api。

  其次可以改善软件质量,支持重构。

  其三,大幅减少debug时间。

  前期投入大,后期能大幅缩短开销,将问题发现在最源头

  提供明确的目标

  你很清楚, 一旦结束(测试通过), 你的工作就完成了(假设你的测试写的很好). 测试代码会为代码建立一个自然的边界, 使你把重点集中在当前任务上. 一旦测试通过, 就有确切的证据证明你的代码能工作. 相对于人工的测试用户界面或者比较日志文件中的结果,  在一个xUnit框架中运行自动化测试, 速度要快几个数量级. 大多数xUnit测试的运行只需几微秒, 而且大多数采用TDD的人都会一天运行数次测试. 在许多开发小组中, 将代码上传配置库前, 必须成功地通过测试。

 提供文档

  你是不是经常遇到看不懂的代码? 这些代码可能没有任何文档说明, 而且开发代码的人可能早就走了(或者去度假了)。 当然看到这种代码的时间往往也很不合时宜, 可能是凌晨3点, 也可能有位副总在你旁边大声催促着赶快解决问题, 这样要想花些时间来愿作者的意图就更困难了。 我们见过一些好的单元测试文档, 它们会指出系统要做什么。 测试就像原开发人员留下的记号, 可以展示他们的类具体是怎么工作的。

  改善设计

  编写测试能改善设计。 测试有助于你从界面的角度思考, 测试框架也是代码的客户。 测试能让你考虑得更简单。 如果你确实遵循了“尽量简单而且行之有效”的原则, 就不会写出篇幅达几页的复杂算法。 要测试的代码通常依赖性更低, 而且相互之间没有紧密的联系, 因为这样测试起来更容易。 当然, 还有一个额外的作用, 修改起来也会更容易!

  鼓励重构

  利用一套健壮的测试集, 你能根据需要进行重构。 你是不是经常遇到一些不知是否该修改的代码? 种种的顾虑让你行动迟缓, 过于保守, 因为你不能保证所做的修改会不会破坏系统。 如果有一套好的单元测试集, 就能放心的进行重构, 同时能保证你的代码依然简洁。

  提高速度

  编写这么多测试会不会使开发速度减慢呢? 人们经常会以速度(或开发开销)作为反对进行TDD和使用xUnit框架的理由。 所有的新的工具都会有学习曲线, 但是一旦开发人员适应了他们选择的框架(通常只需要很短的时间), 开发速度实际上会加快。 一个完备的单元测试集提供了一种方法对系统完成回归测试, 这说明, 增加一个新特性之后, 你不必因为怀疑它会不会破坏原系统而寝食难安。

  提供反馈

  单元测试还有一个经常被忽略的优点, 即开发的节奏。 尽管看上去好像无关紧要, 但通过测试之后你会有一种完成任务的成就感! 你不会成天地修改代码而没有任何反馈, 这种测试-代码-测试的方法会鼓励你动作幅度小一些 通常修改一次代码的时间仅仅几分钟而已。 这样你不会一下子看到冒出一大堆新的特性, 而只是让代码每次前进一小步。

  TDD所带来的好处是否被过度的夸大?

  当需要进行测试时,我信守下面的经验主义的做法:

  ● “先测试”还是“后测试”并不重要,只要你是在测试。

  ● 在你的开发过程中尽可能早的考虑测试。

  ● 不要让某个框框限制了你的行动。例如,不要轻信那些人告诉你的、要写出“尽可能简单的能够运行的程序”—也就是所谓的YAGNI—的话。如果你的经验告诉你,未来你会用到这个额外的类—虽然现在用不着,你应该相信你的判断,加上这个类。

  ● 记住,功能测试是真正对用户有意义的测试。单元测试只是为你—开发者—服务的。属于奢侈品。如果你有时间去写单元测试,那最好了:当你的程序出现问题时,它们能帮助你省去很多时间。但如果你没有时间,你要确保功能测试能覆盖到你的产品里用户所期望的所有功能点。

  ● 如果你没有做驱动测试开发,不要有任何的不安。有太多的因素都能导致这种开发方法在众多的项目和个人开发习惯中水土不服(有很多因素那些TDD极端主义者们永远都不会提)。

  TDD( 测试驱动开发) Overview

  第一篇技术博客,希望有人支持,您的关注是我的动力。。。

  本文主要是基于本人的开发经验,概叙一下TDD,也就是测试驱动开发。我比较喜欢用问题方式来写,语言水平有限 希望读者看得懂且有帮助

  TDD这个东西 你一般用了之后会上瘾:) 它可能改变你以后的编程习惯

  什么是TDD

  故名思意就是用测试的方法驱动开发。简单说就是先写测试代码,再写开发代码,和传统的方式是反的。

为什么要用TDD

  用TDD的方法可以使代码干净(代码重构的结果),测试覆盖率高(先写测试的结果),软件做集成测试的时候一般问题会比较少。

  而且你敢改人家的代码,看到有fail的test case 证明你有改错人家的东西,看到所有的test case都过了的话,你也很有信心说,我没有改错,或程序不会因为我的改动而挂掉。

  什么时候TDD

  TDD是在Unit Test,  也就是单元测试时用的方法。

  什么地方TDD

  我觉得写任何代码都可以用TDD吧

  怎么做TDD(关键5步)

  1、加入一个新的测试

  2、运行下新加的测试,看到它失败(因为你还没写功能代码)

  3、对开发代码做很小的修改,目的就是让新加的测试通过 (注意这里的目的)

  4、运行所有的测试(test case),然后看到所有测试都通过了 (看到测试都变成绿色,一般都会小开心一下)

  5、移掉重复的代码,对代码进行重构 (既包括功能代码,也包括测试代码。特别注意红色的字串 一般会有重复,还有一些代码可以抽出来变成公用方法,测试代码中同样的初始化和还原测试环境的代码,可以放到intilize和cleanup中去)

  而外还有一些步骤也是可以加入的,比方

  ● 在写测试代码前,先从需求出发,准备一个Test list (需要测到的功能的列表)。忘掉你该怎么实现,那是后面的事情

  ● 每测完一个就用横线划掉

  ● 如果发现有漏掉的test 就加到这个列表中(列表测完你的功能也就完成了)

  TDD的好处,和不足的地方

  好处

  ● 测试代码都是从客户需求出发的,不是重实现出发的。测试更关注于对外部的接口。

  ● 软件的需求都被测试代码描叙得很清楚,可以减少很多不必要的文档(有些时候写文档时间比开发时间多多了, 需要一个专门写文档的,而且用的机会很少。这里我很喜欢敏捷开发中说的:Just enough)

  ● 每次都是很小的步骤,这样你就很集中注意解决一个问题。葛优说的:步子迈大了容易扯着蛋,步子大想的就多,容易忽视些东西, 容易出错。小而简单就是美

  ● 可以优化设计。如果有做过测试驱动开发的会发现,为了更好的,更容易的做单元测试。它逼着你面向接口编程和使用一些设计模式,自然设计就灵活了,耦合性也低

  缺点

  ● 有时候开发代码可能只有几行,可是测试代码可能比真正的代码要多很多。而且花时间想怎么测试。

  ● 可能不适合时间很紧的软件开发,更适合于产品和平台的开发

  怎么学习TDD最好

  我觉得最好且最快的方式就是 XP中提到的结对编程,一个有TDD经验的坐在“后面”,指导另一个不大熟悉的人,两人一起来完成一个类或模块的功能

  几个关键点

  ● 记得你是做单元测试,不是集成测试,你要测得仅仅是你的类的功能,不要去测别人类的功能,一定要知道测到什么程度就好了,剩下的可能是别人需要测的

  ● 每次都是一小步,目的只是用最简单的方法让新加的test case过掉而已

  ● 在改/加任何功能代码前,一定要先想是不是要改或加test case。

  ● 测试驱动产生的单元测试代码是代替不了集成测试的,它还是单元测试

  ● 测完记得清理测试环境,还原到测试之前的样子

posted @ 2012-06-21 10:24 顺其自然EVO 阅读(405) | 评论 (0)编辑 收藏

软件项目质量评价方法之一

软件项目质量评价方法之一

发布时间: 2012-6-20 10:42    作者: 云天    来源: TaoBao QA Team 

字体:        | 上一篇 下一篇 | 打印  | 我要投稿  | 推荐标签: 软件测试 质量管理 测试管理

  对项目质量进行评价,是对项目上线前的质量把关,而且可以对项目过程中的质量进行动态的监控,便于尽早发现问题,提高项目质量。

  项目质量评价的一般步骤如下:

  1、建立项目质量目标;2.定义项目质量维度;3.确定评价模型;4.确定基线数据;5.执行项目质量评价;6.对比目标、采取修正行动。

  影响软件项目质量的主要因素会在一些环节产生:产品调研、产品需求分析、PRD设计、UC编写,编码、测试、部署等环节及各个环节的评审。这其中就会由:人的因素、软件需求、测试的局限性、质量管理的困难、软件人员的传统习惯、开发规范、开发工具的支持、各个阶段文档的完整性…等因素造成最终的质量水平。

  目前比较常用的质量标准有:CMM等级、CMMI-PPM过程模型、ANSI/IEEE STOL 730-1984,983-1986标准、国际标准《ISO/IEC 9126软件质量特性》、国家标准《GB/T16260-1996软件产品评价、质量特性及其使用指南》、FCM模型…等。

  由以上各个规范或者标准的核心,可以用下面的公式来表达:

  其中:

  F是质量的最终得分,该得分是对质量的好坏的评定,得分高,代表质量好。

  C 是指用来评价质量的标度,或者度量指标的得分。标度可以是第一级的,也可以是该标度对应的下一级的标度。对于C有一定的细则,根据项目每天的数据、参照该细则,会有一个得分。关于该细则的制定:可以根据各个环节的行业专家权威的经验,制定一个标准。该得分细则一旦制定,就具有一定的权威,以后就可以每次应用。

  M是某一个得分C对应的标度 权重系数。该系数也是由经验得到,并且可以再应用中慢慢修改优化。

  L是评价质量标度的项数。

  最后得出的F,是个具体的、连续的数字,根据经验知识,就可以知道该数据F落入的区间段,从而知道项目质量的好、中、差等。

  在运算的过程总,也会知道各个C的得分,从而知道哪项标度对应的工作存在问题,需要改进。

  整个思维过程很简单,很像大学里面的每个学期求学生的综合素质得分,根据每门课的得分、和每门课的权重、平时课外活动的权重得分,得到一个最后的综合分数。该得分参考“优秀”、“良好”、“一般”、“不及格”等区间对应的分数段,给这个学生一个评价。

posted @ 2012-06-21 10:15 顺其自然EVO 阅读(919) | 评论 (0)编辑 收藏

SQL Server2005中触发器的运用

  编写过存储过程的人,再编写触发器时会发现:他们的语法、格式是非常类似的。其实触发器就是一种特殊类型的存储过程。他们都是预编译的,在程序正式编译前就由编译器进行编译,存储在服务器端。

  不过,触发器与一般的存储过程也有些区别。触发器主要是通过对数据库的增删改的操作,或者是一个触发动作的触发作用等事件触发而被执行;而存储过程则是通过像传递SQL语句一样,传递存储过程的名字来被程序调用,实现功能。

  触发器一共分为五种类型:Update触发器,Insert触发器、Delete触发器、Instead of触发器和After触发器。前三个分别是相应表上进行更新、插入、删除操作时触发。Instead of触发器在不执行插入、更新或删除操作时触发。

  在触发器中存在两个虚拟表:Inserted表和Deleted表。Inserted表保存的是Insert或Update之后所影响的记录形成的表,Deleted保存的是Delete或update之前所影响的记录形成的表。这两个表是逻辑表,这两个表是动态驻留在内存中的,当触发器工作完成,这两个表也被删除。

  触发器的创建代码格式:

CREATE TRIGGER trigger_name      --触发器的名字
ON table|view [WITH ENCRYPTION]  --在哪个表上创建触发器
{ FOR | AFTER | INSTEAD OF }    
{[INSERT][,][UPDATE][,][DELETE]} --激活触发器的类型
AS sql_statements […n]

  代码中关键字for、after、Insteadof分别代表不同的使用范围:

  for表示为AFTER触发器,且该类型触发器仅能在表上创建;

  after表示只有在执行了指定的操作INSERT、DELETE、UPDATE之后触发器才被激活,执行触发器中的SQL语句;

  instead of当为表或视图定义了针对某一操作INSERT、DELETE、UPDATE的INSTEAD OF 类型触发器,且执行了相应的操作时,尽管触发器被触发,但相应的操作并不被执行,而运行的仅是触发器SQL语句本身。

  下面说下触发器的作用:

  1、级联修改数据库中的相关的表;

  看下面的牛腩新闻发布系统的例子:其中一个新闻类别(Category)对应多个或者0条新闻;一条新闻(news)对应着多个或者0个新闻评论(comment)。

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
-- =============================================
-- Author:  刘正权
-- Create date: 2008-11-15 11:13
-- Description: 删除新闻类别触发器
-- =============================================
CREATE TRIGGER trigCategoryDelete
   ON  category
   instead of DELETE
AS
BEGIN
--删除新闻,再在类别表中删除--触发器实现
 declare @caId int
 select @caId=id from deleted
 --删除评论(选出多条用in,一条用=号)
 delete comment where newsId in (select newsId from news wherecaId=@caId)
 --删除新闻
 delete news wherecaId=@caId
 --删除类别
 delete category whereid=@caId
END

  2、执行比核查约束更为复杂的约束操作;在触发器中可以书写更为复杂的SQL语句,例如可以引用多个表,并使用if……else等语句做更复杂的检查。

  下面看下例子:如果更改了学生的学号,则他的借书记录表中记录也同时更新。

  这时候,我们可以建立一下触发器:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:  刘正权
-- Create date:2012-4-22
-- Description: 更改了学生的学号,则他的借书记录表中记录也同时更新
-- =============================================
Create Trigger truStudent
On Student        --在Student表中创建触发器
for Update        --为什么事件触发
As                --事件触发后所要做的事情
if Update(StudentID)           
begin

Update BorrowRecord
Set StudentID=i.StudentID
From BorrowRecord br , Deleted   d ,Inserted i  --Deleted和Inserted临时表
Where br.StudentID=d.StudentID
end 





  3、拒绝或回滚违反引用完整性的操作。检查对数据库的操作是否违反引用完整性,并选择相应的操作;

  看下面的例子:不允许删除任何销售记录大于等于20条的商店。

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:  刘正权
-- Create date: 2012-4-22
-- Description: 不允许删除任何销售记录大于等于20条的商店
-- =============================================
CREATE TRIGGER trDelSales
   ON  tblSales  
   for Delete
AS 
 if(select Count(*) from Deleted 
 where Deleted.qty>=20)>0
BEGIN
 print'您不能删除任何记录'
 rollback transaction   --事务回滚
END
GO

  4、比较表修改前后数据之间的差别,并根据差别才去相应的操作。

  例如:若想规定每次工资的变动幅度不能超过40%,使用触发器可以将修改后的表数据和修改前的表数据进行比较,若超出40%,可以回滚该修改操作。

  触发器是自动触发的,一旦对表中的数据做了修改,该触发器将立即被激活,充分体现了触发的优势,保持了数据的完整性;然而,触发器性能通常是比较低的。

  当运行触发器时,系统处理的大部分时间花费在参照它表达的这一处理上,因为这些表达既不在内存中,也不在数据库设备上,而逻辑表(删除表和插入表)总是位于内存中。所以触发器参照的其他表的位置决定了操作花费时间的长短。

posted @ 2012-06-21 10:00 顺其自然EVO 阅读(2760) | 评论 (2)编辑 收藏

仅列出标题
共394页: First 上一页 312 313 314 315 316 317 318 319 320 下一页 Last 
<2025年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜