Successfully reported this slideshow.
Your SlideShare is downloading. ×

Java与单元测试.ppt

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
有效的单元测试.ppt
有效的单元测试.ppt
Loading in …3
×

Check these out next

1 of 69 Ad

More Related Content

Recently uploaded (20)

Advertisement

Java与单元测试.ppt

  1. 1. Java与单元测试
  2. 2. 目 录 单元测试思想 1 JUnit、DBUnit 2 JMock 3 EclEMMA 4
  3. 3. 单元测试概述 单元测试越来越被看重:  非常不幸:上帝赐予我们青春的同时也赐予了 我们青春痘;我们编出代码的同时也编出了 bug,这几乎是无法避免的  公认事实:bug发现得越早修复的代价越小  勿庸置疑:代码最早被测试的一环就是单元测 试  单元测试:最早测试、最容易发现问题、修复 成本最小
  4. 4. 单元测试基本概念 对一个独立工作单元的测试, java中通常是对一个类或方法的 测试 目的 检查方法的行为是否满足约定 功能 查找代码最简单的错误、帮助确 定系统需求 定义
  5. 5. 举例说明—单元测试确定需求 任何一个需求都可以分解细化成若干测试 用例 假设需求:0:00-7:00 IVR方式下载彩 铃免费  1:00 IVR方式下载彩铃,出零话单  2:00 www方式下载彩铃,照常计费  3:00短信方式下载彩铃,照常计费  8:00 IVR方式下载彩铃,照常计费  23:00 IVR方式下载彩铃,照常计费
  6. 6. 单元测试特点 小步前进——简单 1 外部依赖多——困难 2 测试用例多——麻烦 3 有维护成本 ——讨厌 4
  7. 7. 开发单元测试代码的原则 被测代码宜于测试 1、用接口分离外 部依赖 (依赖的接 口以参数形式传入 ) 2、功能独立、简 洁,减少内部依赖 3、根据自动化单 元测试工具的特点 单元测试代码 测试代码易于测试 1、足够简单,保 证自身的正确性 , 尽量少继承、重载 2、命名清晰、准 确,减少歧义 3、保存成果,重 复利用
  8. 8. 单元测试思想总结 bug发现越早修复代价越小 可用 测试是编码的逆向思维,最容易 验证代码的正确性 易用 借助自动化单元测试工具减少开 发、维护成本 有用
  9. 9. 单元测试工具-帮助解决问题 JUnit EclEMMA DBUnit JMock
  10. 10. 大名鼎鼎的JUnit框架 设计的出发点:  有效抓住开发人员编写代码的意图,然后快速检查 代码是否与原始意图相匹配 要解决的问题:  发现开发人员的代码意图,然后如何使开发人员更 加容易得表达他们的代码意图 能解决的问题(Why Junit):  测试代码的编写、执行变得容易  组织、管理测试代码,降低维护成本  测试代码的成果易于保存并重复利用
  11. 11. JUnit 基础知识 testxxx fixture assert bar 继承了 testcase 测试类的 一个方法, 相当于一 个测试用 例 调用每个 testxxx() 前后固定执 行的环境搭 建和拆除函 数(testcase 调用顺序不 定导致) 检验expect 和actual是 否匹配的系 列函数;通过 assert检验 的用例被认 为是成功的, 否则就认为 是失败 Eclipse“Ru n as JUnit” 执行测试用例, 符合assert表 示成功(显示 绿条)、否则 为失败(蓝条)、 程序抛异常会 报错(红条)
  12. 12. JUnit3.x 印象 fixture testxxx Run framework
  13. 13. JUnit 3.x 示例 public class Junit3 extends TestCase{ protected void setUp() { super.setUp(); //fixture } public void testMul(){ double ret = cal.mul(12.5, 2.0); //assert语句:谓(Equals)+主(ret)+宾(25.0) assertEquals(ret, 25.0); } }
  14. 14. 走进JUnit 4.x(1) 必须Java 5 以上环境 1 不再需要extends testcase类 2 使用annotation注解体现操作 3 4 全局初始化方法@BeforeClass
  15. 15. 全局初始化方法详解  用来初始化测试类中每个测试用例都用到的资源: DB、Socket、DataStructure等,更加灵活  不用在每个测试用例执行之前都重新创建,可以 创建一次,还原一次,大大加快系统运行速度  eg.测试调用第三方库代码的错误处理时,在测 试开始之前重定向 System.err,以防输出被 不预期的消息打乱;在测试结束后还原  当然还有全局资源回收方法@AfterClass
  16. 16. Junit 4 示例 public class junit4 { private static multiple cal; @BeforeClass public static void globalInit(){ cal = new multiple(); } @Before public void init(){ System.out.println("init"); } @Test public void simpleTest(){ int result = cal.mul_int(3, 5); assertThat(result, is(15)); } }
  17. 17. 走进JUnit 4.x(2) 5 异常处理:@Test (expected,timeout) 6 Assume假设机制,提供过虑条件 7 assertThat替代其它assert语句 8 方法名不再有限制,建议保持原习惯
  18. 18. assertThat语句详解  用类自然语言语法主+谓+宾结构精确指定想满 足的条件  用hamcrest.Matchers进行模式匹配  使编码风格统一  默认提供一些可读的描述性错误信息  String s = "hello world!"; assertThat( s, anyOf( containsString("developer"), containsString("Works") ) );  如果出错后,系统会自动抛出以下提示信息: java.lang.AssertionError: Expected: (a string containing "developer" or a string containing "Works") got: "hello world!"
  19. 19. Assume机制和异常处理示例 @Test(expected=Exception.class) public void getException(){ assumeThat(4, is(5)); //assume成功才执行下面的语句 System.out.println("divide by 0"); int result = cal.divide(10, 0); }
  20. 20. 走进JUnit 4.x(3) 9 @SuiteClasses({a.class,b.class}) 10 参数测试:Parameterized. class 11 理论测试:Theories. class 12 @RunWith使用不同runner
  21. 21. Runwith和suite示例 import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; /* * 使用Suite运行器装载测试集 * 指定若干个测试用例,组成一个测试集 * 测试集的执行相当于一起执行被指定的测试用例 */ @RunWith(Suite.class) @SuiteClasses({ConnectToDBTest.class,Downloa dOneRingTest.class}) public class DownloadRingSuite { //函数体是空的 }
  22. 22. 参数测试与理论测试 单个测试用例,为创建的 每套参数运行一次: 通过@Parameters指定 参数列表(测试类成员变 量按顺序取值); @Test指定testcase; 参数次序固定,可指定返 回结果; 若干测试用例,由数据集 排列组合进行检测: 通过@DataPoint序列指 定数据集; 通过@Theories执行 @Theory指定testcase 参数次序由系统排列组 合,指定的是输入参数; 参数测试 理论测试
  23. 23. 参数测试示例 @RunWith(Parameterized.class) public class parameterTest { private int n1, n2, result; public parameterTest(int para1, int para2, int result){ this.n1 = para1; this.n2 = para2; this.result = result; } @Parameters public static Collection parameters(){ return Arrays.asList(new Object[][]{ {1,2,3}, {2,3,5}, {3,4,7} }); } @Test public void testAddWithPara(){ calculator cal = new calculator(); int ret = cal.add(n1, n2); assertEquals(result, ret); } }
  24. 24. JUnit 最佳实践(1) 1 独立:一个测试 类,只测试一个 对象;一个 testxxx,只测试 这个对象的一个 方法,方便定位 出错位置 2 易读:给测试方 法一个合适的名 字 ,如果没有 better choice, 测试类命名为 XXXTest,测试 方法testxxx 3 完备:测试所有 可能引发错误的 地方 ,对于只含 有getter/ setter 的类,如果是由 IDE产生的,才可 不测
  25. 25. JUnit 最佳实践(2) 4 绝缘:在setUp和 tearDown中的代 码不应该是与测 试方法相关的, 而应该是全局相 关的 5 隔离:与被测代 码放相同的包不 同的目录。方便 访问被测试类的 protected变量/方 法,方便被测代 码管理与发布 6 小心:junit.jar最 好不要放到 <JAVA_HOME>/j re/ lib/ext目录
  26. 26. JUnit使用注意事项  Java类加载机制引起  java虚拟机和程序都调用ClassLoader类的 loadClass的方法来加载  ExtClassLoader是AppClassLoader的父类  ExtClassLoader加载<JAVA_HOME>/jre/ lib/ext下的jar包 ( junit.jar )  AppClassLoader加载classpath环境变量指 定的路径中的类 (被测试类)  ExtClassLoader不知AppClassLoader已装 载了被测试类,仍尝试去加载,就会抛异常
  27. 27. JUnit 小结 使用非常简单 1 能指出代码中存在的问题 2 可以作为代码更准确的文档 3 4 在持续集成过程中起重要作用
  28. 28. junit的近亲DBUnit 对JUnit的扩展(还需要junit.jar) 1 目前最新版本2.4.1(定制错误处理) 2 致力于数据库功能的自动化测试 3 4 借助数据集概念实现数据对比
  29. 29. DBUnit初探 工作过程 基本概念 设计目的 用代码检查( assertEqual)对 数据库的操作是否 符合预期(清楚得 知道哪些数据会如 何变化、精确到字 段) 由被测代码产生实 际数据=预期数据 (xml); 代码 的操作固定,需要 一个明确的预置数 据(xml) 预置数 据+代码动作=实 际数据 DatabaseTestCas e、IDataSet (Table、Filter)、 IDatabaseConne ction、Assertion、 DatabaseOperati on、Difference、 FailHandler ?
  30. 30. DBUnit框架基类  DatabaseTestCase  测试类extends的基类  子类必须实现getDataSet() 指定预置数据  子类必须实现getConnection()指定数据库 (driver、url、username、password)  子类可以覆盖getSetUpOperation()指定初始 化操作,缺省是CLEAN_INSERT  子类可以覆盖getTearDownOperation (),缺 省是NONE  以test开头的方法识别为测试用例:获取预期 数据和实际数据,并进行对比
  31. 31. DBUnit核心对象-数据集 IDataSet  ‘I’开头的对象表示接口  本测试流程所关注的数据集合  重要实现FlatXmlDataSet(xml文件数据集)  由ITable对象组成,通过getTable(String tblname)、getTables()获取。  getTableMetaData getTable(String tblname)获 取table的描述信息  用filter进行过虑:IColumnFilter (DefaultColumnFilter . includeColumns /excludeColumns )、ITableFilter (DefaultTableFilter .includeTable/excludeTable)
  32. 32. DBUnit核心对象-数据库连接  IDatabaseConnection  DBUnit对普通数据库连接的简单封装  两个实现:DatabaseConnection(针对jdbc)、 DatabaseDataSourceConnection  普遍用法  两个方法: createDataSet()/createDataSet (String[] ) createQueryTable(String resultTbl,String sql)  相关对象: QueryDataSet (IDatabaseConnection conn) QueryDataSet.addTable(resultTbl, sql); protected IDatabaseConnection getConnection() throws Exception { Class.forName(dbDriver); Connection con = DriverManager.getConnection(dbUrl, dbUsername, dbPassword); return new DatabaseConnection(con); }
  33. 33. DBUnit核心对象-数据库操作 DatabaseOperation  每个testxxx执行前后根据数据集对数据库 做的操作  UPDATE、INSERT、DELETE (数据集里 指定表的指定记录) 、DELETE_ALL(数据 集里指定表的记录)、REFRESH(update, insert,no delete)、CLEAN_INSERT、 NONE
  34. 34. DBUnit核心对象-数据对比 assertion  assertEquals(expectedDataSet, actualDataSet)  assertEquals(expectedTable, actualTable)  assertEquals(expected, actual, FailureHandler) DiffCollectingFailureHandler myHandler; List diffList = myHandler.getDiffList(); Difference diff = (Difference)diffList.get(i); diff.getActualTable()/getExpectedTable() diff.getActualValue()/getExpectedValue() diff.getColumnName()
  35. 35. 通过java app制作预置数据 连接DB 建立 导出 修改
  36. 36. 从数据库导出 xml文件实例 public boolean generateXML() { Connection con = null; try { Class.forName(dbDriver); //传统jdbc连接 con = DriverManager.getConnection(dbUrl, dbUsername, dbPassword); //返回DBUnit封装的数据库连接 IDatabaseConnection conn = new DatabaseConnection(con); String tables[]={"ut_userRings", "ut_chargeLog"}; IDataSet dataSet = conn.createDataSet(tables); FlatXmlDataSet.write(dataSet, new FileOutputStream("xml/dbunit.xml")); } …… }
  37. 37. 两种xml格式文件 <?xml version='1.0' encoding='UTF- 8'?> <dataset> <table name="UNITTEST"> <column>userindex</column> <column>phonenumber</column> <column>createDate</column> <column>loginName</column> <column>passwd</column> <column>account</column> <row> <value>1</value> <value>13812345678</value> <value>2008-10-10</value> <value>EB</value> <value>123456</value> <value>50.0</value> </row> </table> </dataset> xml文件 <?xml version='1.0' encoding='UTF-8'?> <dataset> <UNITTEST userindex="1" phonenumber="13812345678" createDate="2008-10-10" loginName="EB" passwd="123456" account="50.0"/> <UNITTEST userindex="2" phonenumber="13500008888" createDate="2008-10-21" loginName="9527" passwd="123456" account="200.0"/> </dataset> XmlDataSet(易抛异常) FlatXmlDataSet(推荐)
  38. 38. 编写DBUnit测试用例(1) getConnection 连接数据库: 1.提供dbdriver 、url、passwd 、username、 得到conn 2.return new DatabaseConn ection(conn) 3.每个testxxx执 行前都重新连接 标准用法继承 DatabaseTestC ase,获得3个特 性: 1.检测testxxx 2.implement getConnection 、getDataSet 3.每个testxxx前 后执行setup和 teardown 用getDataSet 获取预置数据集 1.file = new FileInputStre am(“xx.xml”) 2.return new FlatXmlDataS et(file) 3.每个testxxx 执行前重新获取 extends connect DataSet
  39. 39. 编写DBUnit测试用例(2) 必须public 必须void 必须test开头 必须无参数; 执行测试的主体 :获取预置数据 与产生的实际数 据进行对比并报 告结果,2.4.x 还可进行可能的 错误处理(替代 直接抛出异常) getSetupOpe ration(): 所有testxxx都 相关的全局性初 始化操作,每个 testxxx执行前 都会调用,缺省 执行 DatabaseOp eration.CLEA N_INSERT getTearDow nOperation ():每个 testxxx执行 完毕都会调用 的资源回收、 复位操作,缺 省执行 DatabaseOp eration.NON E setup testxxx teardown
  40. 40. DBUnit 中testxxx的写法(1) 1 设定预期数据: file =new FileInputStream(“y yy.xml”) expectDSet= new FlatXmlDataSet(file) expectTable = expectDSet.getTeb le(tblname); 2 执行被测代码 : 通过import tested.package或 和被测代码安排在同 一package不同目录 使具备访问权; 预置数据+被测方法 =》实际数据 3 获取实际数据: 1.getConnection. createDataSet()/c reateQueryTable() 2.qDSet = new QueryDataSet(get Connection()), qDSet.addTable()
  41. 41. DBUnit 中testxxx的写法(2) 4 数据过滤: expectedTable = DefaultColumnFilte r.excludedColumns Table(table, new String[]{"userindex ", “createDate"}); include操作返回只 包含指定字段的表 5 数据排序: 已按主键排序; sTable = new SortedTable(table, new String[]{“ab”}) //按ab字段(int)排序 一定要执行sTable. setUseComparable (true); 6 数据对比 : assertEquals( DataSet和DataSet Table和Table); 自动匹配字段; 一旦不相等缺省抛异 常终止所有测试用例 的运行;避免之前就 抛异常而造成假通过
  42. 42. 2.4.x中定制错误处理  错误:assertEquals失败  出错后代码继续执行,可让所有testxxx都完成运行  每次使用都登记:不抛异常,把所有difference都装入 myHandler,可依次取出记录日志  DiffCollectingFailureHandler myHandler=new …(); Assertion.assertEquals(expect, actual, myHandler); List diffList = myHandler.getDiffList(); for(int i=0; i<diffList.size(); i++){ Difference diff = (Difference)diffList.get(i); log(diff.getActualTable()+diff.getColumnName() + diff.getActualValue(); }
  43. 43. 抛开DatabaseTestCase DBUnit标准用法继承DatabaseTestCase:  程序流程固定,但也失去了灵活性  暂时不能使用Junit4提供的新特性 结合Junit4不继承DatabaseTestCase的用法  Junit4提供的新特性使程序灵活性大大增强  仍借助DBUnit提供的数据集对象进行数据之间的 对比,同样达到对数据库操作自动校对的功能  不必严格按照DatabaseTestCase的要求,根据实际情 况可以没有预置数据等要素,但要多完成一些操作 数据集、处理异常等操作  必备要素:数据库连接、数据集操作、数据对比
  44. 44. DBUnit使用小结 预置数据 数据库连接 预期数据 数据对比 实际数据 DB+XML 数据库操作的测试
  45. 45. Why Mock Object 单元测试 Mock Object 简单 粒度小 独立 系统部件 网络连接 Java容器
  46. 46. Mock Object简介 功能:模拟外部依赖,解除耦合因素 1 原理:用反射机制,动态创建虚拟对象 2 工作方式:和Junit配合使用 3 庐山真面目:验证参数返回值调用次数 4
  47. 47. Mock object功能说明 依赖关系 期望结果 系统环境 协作软件 模拟数据 库连接、 网络连接 代替真正 的连接 eg.验证 JDBC连接 在使用结束 后关闭(在特 定时刻调用 java.sql.Co nnection 中 的 close ) eg.模拟 JDBC驱动 程序抛出 的 SQLExce ption 类 尚未开发完 成的协作模 块,提供接 口定义,检 查是否按规 定的次数和 参数调用了 指定方法
  48. 48. JMock简介 最新稳定版本2.5.1 比其它Mock工具更正式、更强大  引进参数匹配:hamcrest.Matchers  返回值更丰富returnValue、returnIterator  设置调用次数、时序(自动机、序列)  自动验证  与JUnit良好集成  语法稍显复杂
  49. 49. hamcrest.Matchers 数值/字串 对象 匹配器 基础匹配 •is,same(obj) •equal,lessThan •stringContaining ,startWith(“eb”) •closeTo(1,0.2) •hasKey,hasItem •any(obj) •nullValue,notNul lValue 组合匹配 •not(Matcher) •anyOf(Matcher1 ,Matcher2……) •allOf(Matcher1, Matcher2……) 模式匹配 参数、对象、返回值
  50. 50. JMock使用(1) JMock测试用例 junit hamcrest jmock integrati on .Test .Assert.* .runner.RunWith .Mockery .Expectations .junit4.JMock .junit4.JUnit4Mo ckery .Matchers.*
  51. 51. JMock测试示例 @RunWith(JMock.class) public class OpenAccountTest{ static Mockery context; @BeforeClass public static void setUpBeforeClass(){ context = new Unit4Mockery(){{ }}; } @Test public void openAccountWithMock(){ final SMS recv= context.mock(SMS.class) context.checking(new Expectations(){{ one(recv).getReplyFromUser(with(equal(phoneNumber))); will(returnValue("Y")); }}); account.openAccount(phoneNumber, recv); }
  52. 52. JMock使用(2) JUnit的Test 方法: @Test public void testxxx() 使用JMock 自己的 Runner: @RunWith( JMock.class ) Mockery context = new JUnit4 Mockry(); xxx aMock =context. mock(xxx. class) 运行器 测试用例 创建对象
  53. 53. JMock使用(3) 执行被测试 代码,结合 mock对象完 成动作,产 生结果 Expectation s:谁的什么 方法用什么 样的参数调 用会返回什 么样的值 检验mock 对象是否按 expectatio ns执行, assertThat 被测代码是 否符合预期 设定期望 产生结果 检验对比
  54. 54. Expectation详解 基本语法:context.checking(new Expectations() {{ expectations go here ... }}); Expectation结构:invocation-count (mock-object).method (argument- constraints); inSequence(sequence- name); when(state-machine.is(state- name)); will(action); then(state- machine.is(new-state-name)); Expectation语句涉及的变量要是final的
  55. 55. Expectation参数(1) invocation-count 调用次数设定  one/oneOf(mock-obj ):执行1次  exactly(n).of(mock-obj ):执行n次  atLeast(n).of(mock-obj ):至少执行n次  atMost(n).of(mock-obj ):最多执行n次  between(n,m).of(mock-obj ):执行n-m次  allowing/ignoring(mock-obj ):执行0-n次  never(mock-obj ) :执行0次,代码可读性
  56. 56. Expectation参数(2) argument-constraints 设定调用参数  不用Matcher:传统参数值,唯一匹配  使用Matcher:with(Matcher),实现精确匹配与 模糊匹配  某个函数的参数要么全用Matcher,要么都不用 action  returnValue:值、对象  returnIterator(List):with(Matcher),实现精确 匹配与模糊匹配  onConsecutiveCalls(returnValue(0),returnValu e(3),returnValue(40)……)  throwException(new Exception(“……”))
  57. 57. Expectation参数(3) sequence按次序执行  final sequence seq=context.sequence(“name”)  one(amock).forward(10);inSequence(seq);  one(amock).turn(45);inSequence(seq); state-machine按时序执行  final States st = context.states(“name”).startsAs(“up”);  one(amock).forward(10); when(st.is( “up”));  one(amock).turn(45); then(st.is(“down”));
  58. 58. JMock测试Generic Type Java的Dynamic Reflection不能与 Generic Type无缝配合  mock如下对象: public interface Juicer<T extends Fruit>{ Liquid juice(T fruit); }  报错写法:Juicer<Orange> orangeJuicer = context.mock(Juicer<Orange>.class,"orange");  告警写法:Juicer<Orange> orangeJuicer = (Juicer<Orange>)context.mock(Juicer.class, “orange”);  可用@SuppressWarnings去除warning
  59. 59. JMock测试Concrete Class JMock使用标准的反射机制,缺省情况下只 能mock Interface 使用ClassImposteriser mock class  不调用被mock类的constructor  $CLASSPATH增加jmock-legacy-2.5.1.jar、 cglib-nodep-2.1_3.jar、objenesis-1.0.jar  import org.jmock.lib.legacy.ClassImposteriser;  Mockery context = new JUnit4Mockery(){{ setImposteriser(ClassImposteriser.INSTANCE);}}
  60. 60. JMock测试多线程 可行:用Executor产生线程 限制:不能new Thread,可能要修改代码 建议:测试非常复杂,得不偿失,最好不用; 结论:多线程测试属于集成测试,不属于单元 测试范畴
  61. 61. Mock可能导致的问题 掩盖 滥用 脆弱 混乱 可能隐藏所 mock的协 作软件中的 缺陷和错误, 集成测试必 不可少 为了能使用 mock测试 可能会导致 滥用 Interface, 只有1个 implement 的interface 是不提倡的 属于白盒测 试,要非常 熟悉类的内 部联系,对 方法的合理 修改可能破 坏mock测试 可能给测试 代码带来混 乱,最好使 用mock的地 方都加上注 释,如果协作 软件完成了 开发可以替 换mock部分
  62. 62. When Mock 具有不可确定行为 非常难创建(含不存在情况) 某些行为难以触发 有用户界面 令运行速度大大减慢 需要确定如何被调用 真实对象
  63. 63. JMock小结 Expectation+hamcrest的匹配器 ,功能非常强大,灵活但复杂,用 时须花费一定精力 好处 有效地加快单元测试的创建和执行 过程 ,最大限度代码保证代码不含 有基础错误 不足 功能上有一定限制,对final class 和multiThread支持有限;滥用 Mock可带来许多副作用 特点
  64. 64. 代码覆盖率统计工具 EclEMMA——简单查看测试效果  定义:执行测试代码,检查被测代码执行覆盖情况  出处:JCoverage的衍生版本,Eclipse+EMMA  安装:Eclipse->update或手工下载解压至Eclipse  使用:launch in coverage mode  结果:对project、package、class进行覆盖率统 计  查看:源代码窗口绿(覆盖),黄(部分覆盖),红(未覆盖)  原理:生成只能由EclEMMA launch的临时class文 件进行分析,一般放在project外的临时目录下; 也可以选择直接对project的class文件进行修改
  65. 65. 用Coverage View查看统计结果 执行后Coverage View产生统计结果  操作session:ReLaunch、删除、合并、选择
  66. 66. Coverage View的下拉菜单 调整分析树从不同的根开始显示  Project>源码目录>package>类 选择不同的统计模式  Block:没有跳转的字节码指令序列,序列的最后一条 指令被执行了就算覆盖;是最基本模式。  Line:class文件中要有行号信息  指令:非debug时近似于行覆盖  Method:至少一个block被覆盖  Type:被load &initialize就算覆盖 过滤功能  隐藏session中没有被load的类
  67. 67. package explorer中显示结果 直接在package explorer中显示统计结果  每个统计元素旁显示green/red bar和覆盖百分比  针对active session  指令覆盖模式  只executable的代码 激活步骤  Windowspreferences  GeneralAppearance  Label decorators  Java code coverage
  68. 68. 单元测试并不解决所有问题 验收测试 性能测试 集成测试 联合测试 单元测试 测试 编码不息,测试不止

×