这段代码将HTML标签改为System.out.println语句输出。MockJSPSearchResult类有两个方法,escapeHTML方法对应result.jsp中的escapeHTML方法,searchResult方法封装了result.jsp中除escapeHTML外的所有代码逻辑。因为JSP需要处理URL请求,所以需要一个MockObject类来模拟URL请求,在代码的第50行由初始化的RequestMockObject对象完成。代码6.6为此对象的代码。
代码6.6 RequestMockObject.java
01 package book.lucene; 02 03 import java.net.URLDecoder; 04 05 public class RequestMockObject { 06 private String query; 07 08 private String maxresults; 09 10 private String startat; 11 12 public RequestMockObject(String query, String maxresults, String startat) { 13 this.query = URLDecoder.decode(query); 14 this.maxresults = URLDecoder.decode(maxresults); 15 this.startat = URLDecoder.decode(startat); 16 } 17 18 public String getParameter(String param) { 19 if ("query".equalsIgnoreCase(param)) { 20 return this.query; 21 } else if ("maxresults".equalsIgnoreCase(param)) { 22 return this.maxresults; 23 } else if ("startat".equalsIgnoreCase(param)) { 24 return this.startat; 25 } else { 26 return null; 27 } 28 } 29 } |
经过这样的处理后,对JSP的单元测试转换为对熟悉的Java类的单元测试。针对MockJSPSearchResult类,画出对应的流程图,设计测试用例进行路径覆盖。我们略去测试用例的设计分析,关于用例的设计前面已经有介绍。这里我们换一种思路,结合代码覆盖率来完善测试用例。测试代码覆盖率的工具采用Cobertura,关于Cobertura,本章不再做详细介绍。感兴趣的读者可以登录http://cobertura.sourceforge. net/index.html了解。
先给出第一个测试用例。
TestCase01
前置条件:索引文件存在,IndexSearcher指向该索引文件。
描述:以hello为关键字,使用默认的搜索设置进行搜索。
输入参数:query=hello,maxresults=100,startat=0。
期待结果:有2条搜索结果。
代码6.7 TestCase01
01 package book.lucene; 02 … 03 public class ResultJSPUnitTest { 04 @Test 05 public void testSearchResult() throws Exception { 06 String[] documentArray = { 07 "Apache Lucene - Building and Installing the Basic Demo", 08 "Apache Lucene - Query Parser Syntax" }; 09 String[] summaryArray = { 10 "Apache > Lucene [Lucene] [Lucene] Main Wiki Lucene 2.3.1 Documentation Documentation Overview Javadocs All Core Demo Contrib Analyzers Ant Bdb Bdb-je Benchmark Highlighter Lucli Memory Miscellane", 11 "Apache > Lucene [Lucene] [Lucene] Main Wiki Lucene 2.3.1 Documentation Documentation Overview Javadocs All Core Demo Contrib Analyzers Ant Bdb Bdb-je Benchmark Highlighter Lucli Memory Miscellane" }; 12 13 MockJSPSearchResult jsp = new MockJSPSearchResult(); 14 jsp.searchResult("hello", "100", "0"); 15 16 Hits hits = jsp.getHits(); 17 assertEquals(2, hits.length()); 18 for (int i = 0; i < 2; i++) { 19 assertEquals(documentArray[i], hits.doc(i).get("title")); 20 assertEquals(summaryArray[i], hits.doc(i).get("summary")); 21 } 22 } 23 } |
这时使用Ant的Cobertura任务,先对编译好的class文件进行插桩,然后运行JUnit测试,最后生成代码覆盖率的报告。下面代码中列出的是Ant的build.xml,代码中列出了覆盖率相关的task,略去了其他内容。
代码6.8 结合Cobertura任务的Ant脚本
01 <?xml version="1.0" encoding="UTF-8"?> 02 03 <project name="lucene" default="coverage" basedir="."> 04 ... 05 <taskdef classpathref="cobertura.classpath" resource="tasks. properties" /> 06 07 ... 08 <target name="instrument" depends="compile"> 09 <delete file="cobertura.ser" /> 10 <delete dir="${instrumented.dir}" /> 11 12 <cobertura-instrument todir="${instrumented.dir}"> 13 <ignore regex="org.apache.log4j.*" /> 14 <fileset dir="${classes.dir}"> 15 <include name="**/*.class" /> 16 <exclude name="**/*Test*.class" /> 17 </fileset> 18 </cobertura-instrument> 19 </target> 20 21 ... 22 <target name="junit" depends="compile"> 23 <mkdir dir="${target.report.dir}" /> 24 <junit printsummary="yes" haltonerror="no" haltonfailure="no" fork="yes"> 25 <classpath location="${instrumented.dir}" /> 26 <classpath> 27 <pathelement location="${target.classes.dir}" /> 28 </classpath> 29 <classpath refid="cobertura.classpath" /> 30 31 <!--list all tests--> 32 <formatter type="plain" usefile="false" /> 33 <test name="book.lucene.ResultJSPUnitTest" todir="${target. report. dir}" /> 34 </junit> 35 </target> 36 37 <target name="coverage-report"> 38 <cobertura-reportformat="html"datafile="cobertura.ser" destdir= "${coverage. html.dir}"> 39 <fileset dir="${src.dir}"> 40 <include name="**/*.java" /> 41 </fileset> 42 </cobertura-report> 43 </target> 44 45 <target name="coverage" depends="instrument,junit, coverage-report" /> 46 </project> |
第5行,定义了cobertura任务。
第8~19行,定义了被测代码的插桩任务。首先cobertura会新建一个cobertura.ser数据文件,用于存放对插桩代码进行代码覆盖分析的数据。Cobertura还会对编译后的class文件进行插桩,并将插桩后class文件放入另一个独立目录中。
第22~35行,运行JUnit测试用例。在classpath中加入插桩后的class目录。
第37~43行,coverage-report任务根据cobertura.ser生成代码覆盖报告。
定义好build.xml后,执行ant命令完成测试代码的构建。生成的代码覆盖率报告如图6.3所示。
图6.3 TestCase01代码覆盖率报告
从报告中可以看出,主要代码MockJSPSearchResult的覆盖率很不理想,行覆盖率只有68%,分支覆盖率只有53%。RequestMockObject代码覆盖率虽然为92%和83%,但是MockObject本身非被测代码,这里重点还是分析MockJSPSearchResult的代码。通过点击报告中MockJSPSearchResult的链接,Cobertura显示了源代码级的覆盖结果,见图6.4。