JUnit 测试

此时,您已经创建了 StockWatcher 应用的初始实现,并且它看起来相当稳定。

随着代码库的发展,如何确保您不会破坏现有的功能?解决方案是单元测试。创建一组完善的单元测试用例是确保应用程序在其生命周期内质量的重要组成部分。

为了帮助您进行测试工作,GWT 提供了与开源 JUnit 测试框架的集成。您将能够创建可以在开发模式和生产模式下运行的单元测试。

在本节中,您将向 StockWatcher 项目添加一个 JUnit 单元测试。

  1. 为 StockWatcher 创建 JUnit 测试类和运行它的脚本
  2. 运行单元测试
  3. 编写单元测试
  4. 解决通过单元测试识别的问题

注意:有关 JUnit 测试的更广泛指南,请参阅 JUnit 测试

开始之前

StockWatcher 项目

本教程基于在 构建示例 GWT 应用程序 教程中创建的 GWT 概念和 StockWatcher 应用程序。如果您尚未完成构建示例 GWT 应用程序教程,并且熟悉基本的 GWT 概念,则可以导入此时编码的 StockWatcher 项目。

  1. 下载 StockWatcher 项目
  2. 解压缩文件。
  3. 将项目导入 Eclipse

    1. 从“文件”菜单中,选择“导入...”菜单选项。
    2. 选择导入源“常规”>“将现有项目导入工作区”。单击“下一步”按钮。
    3. 对于根目录,浏览并选择 StockWatcher 目录(来自解压缩的文件)。单击“完成”按钮。

如果您使用的是 Ant,请编辑 StockWatcher/build.xml 中的 gwt.sdk 属性以指向解压缩 GWT 的位置。

如果您使用的是 Ant,请将 StockWatcher/build.xml 中所有对 path_to_the_junit_jar 的引用替换为指向您系统上 junit 位置的路径。

注意:如果您刚完成构建示例 GWT 应用程序教程,但没有使用 webAppCreator 指定 -junit 选项,请在继续之前,在 StockWatcher/build.xml 中取消注释 javac.teststest.devtest.prodtest 目标,并将所有对 path_to_the_junit_jar 的引用替换为指向您系统上 junit 位置的路径。

创建 JUnit 测试

当您指定 -junit 选项时,webAppCreator 创建了开始开发 JUnit 测试所需的所有文件。它会生成一个入门测试类、从命令行运行测试的 Ant 目标以及 Eclipse 的启动配置文件。

从 GWT 2.0 开始,以前的命令行工具 junitCreator 已与 webAppCreator 合并。

在 Eclipse 中,通过展开 test/ 目录,导航到 StockWatcherTest 类。如果您向现有应用程序添加 JUnit 测试,则需要将 test/ 目录添加为 Eclipse 项目中的源文件夹,并更新您的构建路径以包含对现有 JUnit 库的引用。

  • screenshot: JUnit tests in Package Explorer

检查测试类:StockWatcherTest.java

查看 StockWatcherTest.java 的内容。此测试类是在 StockWatcher/test 目录下 com.google.gwt.sample.stockwatcher.client 包中生成的。您将在其中为 StockWatcher 编写单元测试。目前它包含一个简单的示例测试:testSimple 方法。

  1. 打开 StockWatcherTest.java 文件。
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.junit.client.GWTTestCase;

/**
 * GWT JUnit tests must extend GWTTestCase.
 */
public class StockWatcherTest extends GWTTestCase {                       // (1)

  /**
   * Must refer to a valid module that sources this class.
   */
  public String getModuleName() {                                         // (2)
    return "com.google.gwt.sample.stockwatcher.StockWatcher";
  }

  /**
   * Add as many tests as you like.
   */
  public void testSimple() {                                              // (3)
    assertTrue(true);
  }

}

注意

(1) 与所有 GWT JUnit 测试用例一样,StockWatcherTest 类扩展了 com.google.gwt.junit.client 包中的 GWTTestCase 类。您可以通过扩展此类来创建其他测试用例。

(2) StockWatcherTest 类有一个抽象方法 (getModuleName),它必须返回 GWT 模块的名称。对于 StockWatcher,即 com.google.gwt.sample.stockwatcher.StockWatcher。

(3) StockWatcherTest 类是用一个示例测试用例生成的——一个自相矛盾的测试,testSimple。此 testSimple 方法使用它从 JUnit Assert 类继承的众多 assert* 函数之一,JUnit Assert 类是 GWTTestCase 的祖先。assertTrue(boolean) 函数断言传递的布尔值参数计算结果为 true。如果不是,则当在 JUnit 中运行时,testSimple 测试将失败。

运行单元测试

在开始为 StockWatcher 编写您自己的单元测试之前,请确保测试环境的组件已到位。您可以通过运行 StockWatcherTest.java 来做到这一点,这将执行入门测试 testSimple。

您可以通过四种方式运行 JUnit 测试

  • 从命令行——使用 junitCreator 生成的脚本
  • 在 Eclipse 中——使用 Eclipse 的 Google 插件
  • 在 Eclipse 中——使用 webAppCreator 生成的 Eclipse 启动配置文件
  • 在手动测试模式下

从命令行

GWT webAppCreator 生成的 build.xml 文件包含三个用于测试的生成目标

  • test.dev
    • 在开发模式下运行所有测试类。
  • test.prod
    • 在生产模式下运行所有测试类。
  • test
    • 运行 test.dev 和 test.prod

注意:为了从命令行运行测试,您需要在系统上安装 JUnit。如果您只是下载了 StockWatcher 项目,则必须首先打开 build.xml 并将所有对 /path/to/junit-3.8.1.jar 的引用替换为指向您系统上 junit 的路径。

  1. 在命令 shell 中,浏览到 StockWatcher 目录。
  2. 在开发模式下运行 JUnit 测试。
    • 在命令行中,输入 ant test.dev
  3. 测试以 Java 字节码形式在 JVM 中运行。
    • simpleTest 在没有错误的情况下执行。
[junit] Running com.google.gwt.sample.stockwatcher.client.StockWatcherTest
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 15.851 sec
  1. 在生产模式下运行 JUnit 测试。
    • 在命令行中,输入 ant test.prod
  2. 测试以编译后的 JavaScript 形式运行。
    • simpleTest 在没有错误的情况下执行。
[junit] Running com.google.gwt.sample.stockwatcher.client.StockWatcherTest
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 37.042 sec

在 Eclipse 中(使用 Eclipse 的 Google 插件)

Eclipse 的 Google 插件使在 Eclipse 中运行测试变得容易。

  1. 在开发模式下运行 JUnit 测试。
    • 从包资源管理器中,右键单击要运行的测试用例,选择“以...运行”>“GWT Junit 测试”。
  2. simpleTest 在没有错误的情况下执行。
    • screenshot: JUnit tests in Eclipse
  3. 在生产模式下运行 JUnit 测试。
    • 从包资源管理器中,右键单击要运行的测试用例,选择“以...运行”>“GWT Junit 测试(生产模式)”。
  4. simpleTest 在没有错误的情况下执行。

在 Eclipse 中(使用生成的启动配置)

您可以使用 webAppCreator 为开发模式和生产模式生成的启动配置在 Eclipse 中运行单元测试。

  1. 在开发模式下运行 JUnit 测试。
    • 从 Eclipse 菜单栏中,选择“运行”>“运行配置...”
  2. 在“运行”窗口中,选择“StockWatcherTest-dev”。
  3. 如果您使用的是 Mac OSX,请添加调用 Java 虚拟机的参数。
    • 显示“参数”选项卡。
    • 在 VM 参数中,输入 -XstartOnFirstThread -Xmx256M
    • 要将更改保存到“参数”,请按“应用”。
    • 要运行测试,请按“运行”。
  4. simpleTest 在没有错误的情况下执行。
    • screenshot: JUnit tests in Eclipse
  5. 在生产模式下运行 JUnit 测试。
    • 从 Eclipse 菜单栏中,选择“运行”>“运行配置...”
  6. 在“运行”窗口中,选择“StockWatcherTest-prod”。
  7. 如果您使用的是 Mac OSX,请添加调用 Java 虚拟机的参数。
    • 显示“参数”选项卡。
    • 在 VM 参数中,输入 -XstartOnFirstThread -Xmx256M
    • 要将更改保存到“参数”,请按“应用”。
    • 要运行测试,请按“运行”。
  8. simpleTest 在没有错误的情况下执行。

在手动测试模式下

如果您想选择要运行单元测试的浏览器,请使用手动测试模式。

在手动测试模式下,JUnitShell 主类照常在指定的 GWT 模块上运行,但它不会立即运行测试,而是打印出 URL 并等待浏览器连接。您可以手动将此 URL 剪切粘贴到您选择的浏览器中,单元测试将在该浏览器中运行。

深入:要了解如何在手动模式下运行单元测试,请参阅开发者指南,创建测试用例

编写单元测试

在实际的测试场景中,您可能希望验证尽可能多的 StockWatcher 行为。您可以向 StockWatcherTest 类添加许多单元测试。您将以公共方法的形式编写每个测试。

如果您有大量的测试用例,您可以通过将它们分组到不同的测试类来组织它们。

但是,为了学习在 GWT 中设置 JUnit 测试的过程,在本教程中,您将只编写一个测试并运行它。

  1. 编写一个 JUnit 测试来验证 StockPrice 类的构造函数是否正确设置了新对象的实例字段。
    • 在 StockWatcherTest 类中,添加如下所示的 testStockPriceCtor 方法。
/**
 * Verify that the instance fields in the StockPrice class are set correctly.
 */
public void testStockPriceCtor() {
  String symbol = "XYZ";
  double price = 70.0;
  double change = 2.0;
  double changePercent = 100.0 * change / price;

  StockPrice sp = new StockPrice(symbol, price, change);
  assertNotNull(sp);
  assertEquals(symbol, sp.getSymbol());
  assertEquals(price, sp.getPrice(), 0.001);
  assertEquals(change, sp.getChange(), 0.001);
  assertEquals(changePercent, sp.getChangePercent(), 0.001);
}
  1. 在开发模式下重新运行 StockWatcherTest。
    • 两个测试都应该通过。
[junit] Running com.google.gwt.sample.stockwatcher.client.StockWatcherTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 16.601 sec

解决单元测试中发现的问题

要查看单元测试失败时会发生什么,您将重新引入您在 构建示例 GWT 应用程序 教程中修复的算术错误。最初,变化百分比在 getChangePercent 方法中计算不正确。为了使单元测试失败,您将再次破坏这段代码。

  1. 在 StockWatcher 中引入错误。
    • 在 StockPrice.java 中,进行如下突出显示的更改。
public double getChangePercent() {
  return 10.0 * this.change / this.price;
}
  1. 在开发模式下运行 StockWatcherTest(在 Eclipse 中或从命令行运行)。
    • JUnit 标识了失败的测试用例 (testStockPriceCtor)。(在 Eclipse 中运行时的输出。)
    • screenshot: JUnit tests failed
    • …并为导致异常的异常提供完整的堆栈跟踪——AssertionFailedError。(从命令行运行时的输出。)
Testsuite: com.google.gwt.sample.stockwatcher.client.StockWatcherTest
Tests run: 2, Failures: 1, Errors: 0, Time elapsed: 16.443 sec

Testcase: testSimple took 16.238 sec
Testcase: testStockPriceCtor took 0.155 sec
  FAILED
Remote test failed at 172.29.212.75 / Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
 expected=2.857142857142857 actual=0.2857142857142857 delta=0.0010
junit.framework.AssertionFailedError: Remote test failed at 172.29.212.75 / Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
 expected=2.857142857142857 actual=0.2857142857142857 delta=0.0010
  at com.google.gwt.sample.stockwatcher.client.StockWatcherTest.testStockPriceCtor(StockWatcherTest.java:38)
  at com.google.gwt.sample.stockwatcher.client.__StockWatcherTest_unitTestImpl.doRunTest(__StockWatcherTest_unitTestImpl.java:7)
  ...

注意:从命令行运行时,完整的测试报告(包括堆栈跟踪)将位于 reports/htmlunit.dev/ 目录(用于开发模式测试)和 reports/htmlunit.prod/ 目录(用于生产模式测试)。3. 修正错误。4. 重新运行测试。* 两个 JUnit 测试都应该再次成功完成。

[junit] Running com.google.gwt.sample.stockwatcher.client.StockWatcherTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 16.114 sec

最佳实践:由于 GWT 应用程序在编译为 JavaScript 时和作为 Java 字节码运行时之间的工作方式可能存在细微差异,请确保在开发应用程序时在开发模式和生产模式下运行您的单元测试。不过请注意,如果您的测试用例在生产模式下运行时失败,您将不会获得在开发模式下看到的完整堆栈跟踪。

关于测试的更多信息

此时,您已为 StockWatcher 创建了一个 JUnit 测试,并向其中添加了一个简单的测试用例。

要了解有关 GWT 中所有类型单元测试的更多信息,请参阅开发者指南