技术开发 频道

使用Grester简化Java应用程序JUnit测试

  那么,您已经得到了一个精心编织的 Maven 项目,并且希望在单元测试(或者至少在一组测试中)中测试 Jester。无论是单元测试还是集成测试,明智的做法是要么把项目复制到文件系统中的某个位置并对该副本运行 Jester,要么使用现有副本,但是要准备好恢复对代码源文件所做的所有更改。这是因为 Jester 将更改现有的源代码文件,保存更改并重新编译代码(保留同一个目录中的类文件作为源文件)。如果项目拥有的代码库相对较小或者所选测试很少,则可以使用现有代码库副本。

  在 Eclipse 中设置示例文件

  以测试为例,您将使用在 Eclipse IDE 中构造和准备的基本 Maven 项目。虽然 参考资料 部分包含关于如何操作的链接和信息,但是如何在特定开发环境内构造 Maven 项目和创建必要文件不在本文讨论范围内。图 9 演示了 Eclipse IDE 内的项目。

  图 9. Eclipse IDE 中的示例 Maven 项目

  举例来说,在项目中使用一个相对简单的类和测试类。该类将使用 Java 语言处理外部过程命令的执行。清单 3 显示了测试的类的主要部分。

  清单 3. 在 Maven 项目中测试的示例类

1 package com.prometheus.run;
2 import java.io.IOException;
3 import java.io.InputStream;
4 public class CommandExecutor extends Executor{
5     ...
6     public String executeCommand(String command){
7         ...
8         try {
9             Process child = performCommandExecution(command);
10             stream = child.getInputStream();
11             sb = processStream(stream);
12             ...
13         }
14             ...
15         return sb.toString();
16     }
17     protected StringBuffer processStream(InputStream stream) throws IOException {
18          ...
19          sb = new StringBuffer();
20         while ((c = stream.read()) != -1) {
21                 sb.append((char)c);
22                 }
23         return sb;
24     }
25    ...
26 }
27

  在 CommandExecutor 类中,executeCommand() 方法将调用同一个类 processStream() 中的受保护方法。在 processStream() 方法中,将在 while() 循环中创建一个新 StringBuffer 实例并且处理 InputStream。清单 4 显示了测试类,还显示了测试的主要部分。

  清单 4. Maven 项目中的示例测试类

1   package com.prometheus.run;
2 import com.prometheus.run.CommandExecutor;
3 ...
4 public class CommandExecutorTest extends TestCase {
5     ...
6     public class MockProcess extends Process{
7         ...
8         public InputStream getInputStream(){
9             String source= "This is a mock string";
10             return new ByteArrayInputStream(source.getBytes());
11           }
12         public OutputStream getOutputStream(){
13             return null;
14         }
15         public int waitFor(){
16             return 1;
17         }
18     }
19     public void testExecuteCommmand(){
20         String expected = "This is a mock string";
21         String actual = commandExecutor.executeCommand("lsmod");
22         assertEquals(expected, actual);
23         ...
24     }
25   }
26

  测试类 CommandExecutorTest 相对简单。虽然给出的详细信息不多,但是此单元测试的基本目标是在测试时通过类的 performCommandExecution() 方法调用来模拟 Process 类的行为。

  必须注意的是,要让 Grester 成功运行,项目必须编译代码源文件和测试源文件并成功运行任意一个测试和所有测试(注:由于这个原因,test-compile Maven 阶段将标记允许 Grester 运行且不能提前运行的阶段)。下一步是简单地在项目的 pom.xml 文件中附加 Grester 的 Maven 插件配置。此配置放在 pom.xml 文件的默认构建部分中或任何常规的 Maven 配置文件中。

  把 Grester 与项目联系在一起

  清单 5 显示了放在示例项目的 pom.xml 文件中的 Grester 插件的示例配置。注意,groupId 要对应于 org.apache.Maven.plugins 并且版本应该是最新的 Grester 插件:V0.3。

  清单 5. 示例项目中的 Grester 插件配置

1 <plugins>
2 ...
3 ...
4 <!-- START MAVEN GRESTER PLUG-IN CONFIGURATION -->
5 <plugin>
6 <groupId>org.apache.Maven.plugins</groupId>
7 <artifactId>Maven-Grester-plugin</artifactId>
8 <version>0.3</version>
9 <configuration>
10 <codeSources>src/main/java/com/prometheus/run</codeSources>
11 <testSuiteClass>com.prometheus.run.CommandExecutorTest</testSuiteClass>
12 </configuration>
13 <executions>
14 <execution>
15 <id>inspectSourcesCodeWithGrester</id>
16 <phase>test</phase>
17 <goals>
18 <goal>inspect</goal>
19 </goals>
20 </execution>
21 </executions>
22 </plugin>
23 <!-- END MAVEN GRESTER PLUG-IN CONFIGURATION -->
24 ...
25 </plugins>
26

  注意,项目已被设为在 Maven 的测试阶段运行 Grester 的 inspect 目标。codeSources 将指向包含测试类 CommandExecutorTest 的源代码的目录。它可以像简单地指向实际类 CommandExecutor 一样排除文件扩展名。在 Grester 附带的 README.txt 文件中提到了扩展名 .Groovy,但是应当注意的是,目前没有对 Grester 的支持。

  从 V0.3 alpha 开始,可以在所有有效的 Maven 生命周期阶段中作为插件执行的 Grester 有两个主要目标(使用时全小写):

  inspect —— 这是 Grester 的主要目标,通常在测试阶段(虽然严格来说,它可以是测试编译阶段之后的任意阶段)执行。Grester 将通过 pom.xml 文件中列出的依赖关系创建一个可变的 Java 类路径并把新类路径提供给 Jester。

  help —— 此目标主要用于对正确插件语法和结构的参考,可以在命令行中输入 mvn grester:help 单独执行。

  对示例项目运行 Grester

  运行简单的 mvn clean install 命令(或者包含 inspect 目标使用的特定状态的所有生命周期命令)将生成如下所示的输出。

  图 10. Jester 在处理示例代码

  通过进一步检查,您可以看到初始类文件 CommandExecutor 中的第 27 行已经从 -1 更改为 1。Jester 对单个类执行一个完整操作需要花费一些时间。在操作结束时将生成 jesterReport.xml 文件,该文件显示在 Java Swing 窗口中所发生情况的汇总详细信息。

  寻求 Grester 帮助

  通过命令行运行 mvn grester:help 将生成类似于图 11 的输出。它将用作配置 Grester 的简短指南,而无需参考初始的 README.txt 文件。

  图 11. Grester 的帮助目标

  结束语

  Grester 不是完美的插件,并且仍然在改进中。对 Groovy 源代码的直接支持特别有帮助。同样的概念可以应用到不使用 Maven 但需要构造 Java 类路径字符串(例如,跨越单个文件系统中的多个目录列出依赖关系的 Apache Ant 构建文件)的项目。如果 Ant 文件本身已经被分成许多独立的文件,那么该过程可能会更加复杂。

  对于在一个位置中无法轻松识别其依赖关系的项目,运行单个工具 (Jester) 所带来的麻烦是否值得您去承受。但是,我仍然觉得 Jester 是用于考察开发人员编写测试的方法是否具有健壮性的重要工具。确实,对于使用一组静态测试即可找出的重大代码库更改,当 Jester 报告显示出很差的单元测试或集成测试性能时,开发人员的测试驱动开发(Test-Driven Development,TDD)和行为驱动开发(Behaviour-Driven Development,BDD)技能会让人产生怀疑。

0
相关文章