
在 NetBeans IDE 中编写 JUnit 测试
本教程介绍在 NetBeans IDE 中编写和运行 JUnit 测试的基础知识。测试应用程序是开发周期中不可缺少的一部分,并且编写和维护测试单元有利于确保源代码中的方法能正常运行。IDE 集成了对 JUnit 单元测试框架的支持,它允许您快速和轻松地创建 JUnit 测试和测试套件。
在本教程中,您将为 Java 类库项目创建简单的 JUnit 3 和 JUnit 4 单元测试及测试套件。教程的第 1 部分演示如何在 JUnit 3 中创建测试。第 2 部分演示如何使用 JUnit 标注在 JUnit 4 中创建相同的测试。这两个部分创建的测试是相同的,因此没有必要同时完成两者,但是查看测试的两种编写方法可以帮助您了解 JUnit 4 中引入的一些变化。
有关使用 JUnit 的更多信息,请参见 www.junit.org。
本文档针对的是 NetBeans IDE 6.5 发行版。
目录

要学习本教程,您需要具备以下软件和资源。
创建项目
要完成本教程,您首先需要创建一个名称为 JUnit-Sample 的 Java 类库项目。
创建 Java 类库项目
- 从主菜单选择“文件”>“新建项目”。
- 从“Java”类别中选择“Java 类库”并单击“下一步”。
- 键入 JUnit-Sample 作为项目名称并设置项目位置。
- 取消选中“使用专用文件夹存储库”选项(如果该选项处于选中状态)。
在本教程中,我们将项目库复制到一个专门的文件夹中,因为需要与其他用户或项目共享库。此选项对于 NetBeans IDE 6.0 不可用。
单击“下一步”。
- 单击“完成”。
创建项目之后,如果您查看“项目”窗口中的“测试库”节点,您会看到项目包含 JUnit 3 和 JUnit 4 库。缺省情况下,IDE 会在新项目中添加这两个库。第一次创建 JUnit 测试时,IDE会提示您选择一个版本,然后删除不需要的库。
创建 Java 类
在本练习中,您将下载样例项目并将 Utils.java 和 Vectors.java 文件复制到您创建的类库项目中。
- 右键单击“项目”窗口中的“源包”节点,然后从弹出式菜单中选择“新建”>“Java 包”。
- 键入 sample 作为包名。单击“完成”。
- 下载并解压 JUnitSampleSol 项目,然后在 IDE 中打开该项目。
- 将 JUnitSampleSol 项目 Source Packages 文件夹中的 Utils.java 和 Vectors.java 复制到 JUnit-Sample 的 sample 源包中。
如果您查看类的源代码,您可以看到 Utils.java 包含三个方法(computeFactorial、concatWords 和 normalizeWord),并且 Vectors.java 包含两个方法(equals 和 scalarMultiplication)。接下来,为各类创建测试类并为这些方法编写一些测试用例。
编写 JUnit 3 单元测试
在本节中,您将为 Vectors.java 和 Utils.java 类创建基本的 JUnit 3 单元测试。 您将使用 IDE 根据您项目中的类来创建框架测试类。然后,您将修改生成的测试方法并添加新测试方法。
第一次使用 IDE 在项目中创建测试时,IDE 将提示您选择一个 JUnit 版本。 您选择的版本将成为缺省的 JUnit 版本,并且 IDE 将为该版本生成所有后续测试和测试套件。
为 Vectors.java 创建测试类
在本练习中,您将为 Vectors.java 创建一个 JUnit 测试框架。
- 右键单击 Vectors.java 并选择“工具”>“创建 JUnit 测试”。
- 在“选择 JUnit 版本”对话框中,选择“JUnit 3.x”。
当您选择 JUnit 3.x 时,IDE 将从项目中删除 JUnit 4 库。
- 在“创建测试”对话框中,将测试类的名称修改为 VectorsJUnit3Test。
更改测试类的名称之后,您将看到一个关于修改名称的警告。缺省名称基于要测试的类名,并在该名称后面附加单词 Test。举例来说,对于 MyClass.java 类,测试类的缺省缺省为 MyClassTest.java。 通常,最好是保留缺省名称,但是在教程中您将更改该名称,因为您还将在相同的包中创建 JUnit 4 测试,并且测试类的名称必须是惟一的。
- 取消选中“测试初始化函数”和“测试释放方法”单击“确定”。
单击确定之后,IDE 将在 sample 测试包目录中创建一个 JUnit 测试框架。
项目需要一个目录供测试包创建测试。测试包目录的缺省位置为项目的根目录,但是根据项目的不同,您可以在项目的“属性”对话框中为目录指定不同的位置。
在编辑器中查看生成的测试类 VectorsJUnit3Test.java,您可以看到 IDE 为 equal 和 scalarMultiplication 方法生成了以下具备测试方法的测试类。
public class VectorsJUnit3Test extends TestCase { /** * Test of equal method, of class Vectors. */ public void testEqual() { System.out.println("equal"); int[] a = null; int[] b = null; boolean expResult = false; boolean result = Vectors.equal(a, b); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. fail("The test case is a prototype."); }
/** * Test of scalarMultiplication method, of class Vectors. */ public void testScalarMultiplication() { System.out.println("scalarMultiplication"); int[] a = null; int[] b = null; int expResult = 0; int result = Vectors.scalarMultiplication(a, b); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. fail("The test case is a prototype."); } }
各生成测试的方法主体是作为指导单独提供的,因此需要将它们修改为实际的测试用例。如果您不需要生成的代码,可以在“创建测试”对话框中取消选中“缺省方法主体”。
IDE 生成测试方法的名称时,各方法名称将前面附加一个 test,因为 JUnit 3 使用命名约定和反射来标识测试。要标识测试方法,各测试方法需要遵循 test<NAME> 语法。
在 JUnit 4 中,不再需要使用这种测试方法命名语法,因为您可以使用标注来标识测试方法,并且测试类不再需要扩展 TestCase。
为 Vectors.java 编写测试方法
在本练习中,您将修改生成的方法以实现测试功能,并修改缺省的输出消息。您不需要修改运行测试的输出消息,但是您可能希望修改输出来帮助标识显示在“JUnit 测试结果”输出窗口中的结果。
- 在编辑器中打开 VectorsJUnit3Test.java。
- 修改 testScalarMultiplication 的测试框架,方法是修改 println 的值并删除生成的变量。现在,测试方法应如下所示(粗体为更改部分):
public void testScalarMultiplication() { System.out.println("* VectorsJUnit3Test: testScalarMultiplication()"); assertEquals(expResult, result); }
- 现在,添加一些断言来测试方法。
public void testScalarMultiplication() { System.out.println("* VectorsJUnit3Test: testScalarMultiplication()"); assertEquals( 0, Vectors.scalarMultiplication(new int[] { 0, 0}, new int[] { 0, 0})); assertEquals( 39, Vectors.scalarMultiplication(new int[] { 3, 4}, new int[] { 5, 6})); assertEquals(-39, Vectors.scalarMultiplication(new int[] {-3, 4}, new int[] { 5,-6})); assertEquals( 0, Vectors.scalarMultiplication(new int[] { 5, 9}, new int[] {-9, 5})); assertEquals(100, Vectors.scalarMultiplication(new int[] { 6, 8}, new int[] { 6, 8})); }
该测试方法使用 JUnit assertEquals 方法。要使用断言,您需要提供输入变量和预期的结果。在运行被测试的方法时,要通过测试,测试方法必须根据提供的变量成功返回所有预期的结果。您应该添加足够数量的断言来涵盖各种可能的排列。
- 修改 testEqual 的测试框架:删除生成的方法主体并添加以下 println。
System.out.println("* VectorsJUnit3Test: testEqual()");
现在,测试方法应如下所示:
public void testEqual() { System.out.println("* VectorsJUnit3Test: testEqual()"); }
- 修改 testEqual 方法:添加以下断言(显示为粗体)。
public void testEqual() { System.out.println("* VectorsJUnit3Test: testEqual()"); assertTrue(Vectors.equal(new int[] {}, new int[] {})); assertTrue(Vectors.equal(new int[] {0}, new int[] {0})); assertTrue(Vectors.equal(new int[] {0, 0}, new int[] {0, 0})); assertTrue(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 0, 0})); assertTrue(Vectors.equal(new int[] {5, 6, 7}, new int[] {5, 6, 7}));
assertFalse(Vectors.equal(new int[] {}, new int[] {0})); assertFalse(Vectors.equal(new int[] {0}, new int[] {0, 0})); assertFalse(Vectors.equal(new int[] {0, 0}, new int[] {0, 0, 0})); assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 0})); assertFalse(Vectors.equal(new int[] {0, 0}, new int[] {0})); assertFalse(Vectors.equal(new int[] {0}, new int[] {}));
assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 0, 1})); assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 1, 0})); assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {1, 0, 0})); assertFalse(Vectors.equal(new int[] {0, 0, 1}, new int[] {0, 0, 3})); }
此方法使用 JUnit assertTrue 和 assertFalse 方法来测试各种可能的结果。要通过此方法的测试,assertTrue 必须全部为 true,并且 assertFalse 必须全部为 false。
比较:为 Vectors.java 编写测试方法(JUnit 4)
为 Utils.java 创建测试类
现在,您可以为 Utils.java 创建测试框架。在上一练习中创建了测试之后,IDE 会提示您选择 JUnit 的版本。 但这次,并没有提示要求您选择版本。
- 右键单击 Utils.java 并选择“工具”>“创建 JUnit 测试”。
- 在对话框中,选中“测试初始化函数”和“测试释放方法”(如果为未选中状态)。
- 在“创建测试”对话框中,将测试类的名称修改为 UtilsJUnit3Test。单击“确定”。
单击“确定”之后,IDE 将在“测试包”>"samples" 目录中创建测试文件UtilsJUnit3Test.java。可以看到,除了为 Utils.java 中的方法创建 testComputeFactorial、testConcatWords 和 testNormalizeWord 测试框架之外,IDE 还创建了测试初始化函数方法 setUp 和测试释放方法 tearDown。
为 Utils.java 编写测试方法
在本练习中,您将添加一些测试用例来演示一些常用的 JUnit 测试元素。您还将在方法中添加 println,因为一些方法缺省不打印任何输出。通过在方法中添加 println,您可以稍后在 JUnit 测试结果窗口中查看方法是否已经运行以及它们运行的顺序。
测试初始化函数和释放方法
setUp 和 tearDown 方法用于初始化和释放测试条件。您不需要使用 setUp 和 tearDown 方法来测试 Utils.java,但是此处演示了它们的运行原理。
setUp 方法是一个测试初始化方法,它在测试类中的各测试用例之前运行。运行测试不需要测试初始化方法,但是,如果您需要在运行测试之前初始化一些变量,则可以使用测试初始化方法。
setUp 方法是一个测试释放方法,它在测试类中的各测试用例之后运行。运行测试不需要测试释放方法,但是,您可能需要使用释放方法来清理运行测试用例时所需的任何数据。
- 执行以下更改(显示为粗体),在各方法中添加一个 println。
@Override protected void setUp() throws Exception { super.setUp(); System.out.println("* UtilsJUnit3Test: setUp() method"); }
@Override protected void tearDown() throws Exception { super.tearDown(); System.out.println("* UtilsJUnit3Test: tearDown() method"); }
运行测试时,各方法的 println 文本将出现在“JUnit 测试结果”输出窗口中。如果您没有添加 println,则不会在输出中显示方法已经运行。
在测试中使用简单的断言
这个简单的测试用例将测试 concatWords 方法。 您没有使用生成的 testConcatWords 测试方法,而是使用新的 testHelloWorld 测试方法,该方法使用单个简单的断言来测试方法是否正确地连接了字符串。测试用例中的 assertEquals 将使用 assertEquals(EXPECTED_RESULT, ACTUAL_RESULT) 语法来测试预期结果是否等于实际结果。在本例中,如果 concatWords 方法的输入为 "Hello"、", ","world" 和 "!",则预期结果应该等于 "Hello, world!"。
- 删除生成的测试方法 testConcatWords。
- 添加以下方法来测试concatWords 方法。
public void testHelloWorld() { assertEquals("Hello, world!", Utils.concatWords("Hello", ", ", "world", "!")); }
- 添加一个 println 语句,用于在“JUnit 测试结果”窗口中显示关于测试的文本。
public void testHelloWorld() { System.out.println("* UtilsJUnit3Test: test method 1 - testHelloWorld()"); assertEquals("Hello, world!", Utils.concatWords("Hello", ", ", "world", "!"));
比较:在测试中使用简单的断言(JUnit 4)
在测试中使用超时
此测试演示如何检查方法的完成是否花费了过长的时间。如果方法花费了过长的时间,则测试线程将中断并导致测试失败。您可以在测试中指定时间限制。
测试方法将调用 Utils.java 中的 computeFactorial 方法。您可以假定 computeFactorial 方法是正确的,但是在本例中,您希望测试计算是否是在 1000 毫秒之内完成的。computeFactorial 线程和测试线程是在相同时间启动的。测试线程将在 1000 毫秒过后停止并抛出一个 TimeoutException 异常,除非 computeFactorial 线程先于它完成。您将添加一条消息,这样在抛出 TimeoutException 时会显示该消息。
- 删除生成的测试方法 testComputeFactorial。
- 添加 testWithTimeout 方法,该方法用于计算随机生成数的阶乘。
public void testWithTimeout() throws InterruptedException, TimeoutException { final int factorialOf = 1 + (int) (30000 * Math.random()); System.out.println("computing " + factorialOf + '!');
Thread testThread = new Thread() { public void run() { System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); } }; }
- 修复导入,以导入 java.util.concurrent.TimeoutException。
- 在方法中添加以下代码(显示为粗体),其作用是当测试任务的执行时间过长时中断线程并显示一条消息。
Thread testThread = new Thread() { public void run() { System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); } };
testThread.start(); Thread.sleep(1000); testThread.interrupt();
if (testThread.isInterrupted()) { throw new TimeoutException("the test took too long to complete"); } }
在抛出超时之前,您可以通过修改 Thread.sleep 代码行来更改毫秒数。
- 添加以下 println(显示为粗体),用于在“JUnit 测试结果”窗口中输出关于测试的文本。
public void testWithTimeout() throws InterruptedException, TimeoutException { System.out.println("* UtilsJUnit3Test: test method 2 - testWithTimeout()"); final int factorialOf = 1 + (int) (30000 * Math.random()); System.out.println("computing " + factorialOf + '!');
比较:在测试中使用超时(JUnit 4)
针对预期异常的测试
此测试演示如何针对预期异常进行测试。如果未抛出指定的预期异常,则会导致方法失败。在本例中,您将测试当输入变量为负数时(-5),computeFactorial 方法是否会抛出 IllegalArgumentException。
- 添加以下 testExpectedException 方法,它将以输入 -5 调用 computeFactorial 方法。
public void testExpectedException() { try { final int factorialOf = -5; System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); fail("IllegalArgumentException was expected"); } catch (IllegalArgumentException ex) { } }
- 添加以下 println(显示为粗体),用于在“JUnit 测试结果”窗口中输出关于测试的文本。
public void testExpectedException() { System.out.println("* UtilsJUnit3Test: test method 3 - testExpectedException()"); try {
比较:针对预期异常的测试(JUnit 4)
禁用测试
此测试演示如何临时禁用测试方法。在 JUnit 3 中,如果某个方法名称没有以 test 开头,则它不会被识别为测试方法。在本例中,您将 DISABLED 置于测试方法的名称之前来禁用它。
- 删除生成的测试方法 testNormalizeWord。
- 在测试类中添加以下测试方法:
public void testTemporarilyDisabled() throws Exception { System.out.println("* UtilsJUnit3Test: test method 4 - checkExpectedException()"); assertEquals("Malm\u00f6", Utils.normalizeWord("Malmo\u0308")); }
测试方法 testTemporarilyDisabled 将在您运行测试类时运行。
- 将 DISABLED_(显示为粗体)置于测试方法的名称之前。
public void DISABLED_testTemporarilyDisabled() throws Exception { System.out.println("* UtilsJUnit3Test: test method 4 - checkExpectedException()"); assertEquals("Malm\u00f6", Utils.normalizeWord("Malmo\u0308")); }
比较:禁用测试(JUnit 4)
现在,您已经编写了测试。接下来,您可以运行测试并在“JUnit 测试结果”窗口中查看测试输出。
运行测试
运行 JUnit 测试之后,结果将显示在 IDE 的“JUnit 测试结果”窗口中。您可以单独运行 JUnit 测试类,或者也可以从主菜单中选择“运行”>“测试 PROJECT_NAME”来运行项目的所有测试。如果您选择“运行”>“测试”,则 IDE 将运行“测试包”文件夹中的所有测试类。要运行单独的测试类,右键单击“测试包”节点下的测试类并选择“运行文件”。
- 从主菜单中选择“运行”>“测试 JUnit-Sample”。
运行测试之后,您将在“JUnit 测试结果”窗口中看到以下结果之一:
在此图像中(单击图像查看大图),您可以看到项目通过了所有测试。左侧窗格显示各测试方法的结果,右侧窗格显示测试输出。如果您查看输出,您可以看到测试运行的顺序。添加到各测试方法中的 println 在输出窗口中输出测试的名称。您还可以看到,在 UtilJUnit3Test 中,setUp 方法在各测试方法之前运行,而 tearDown 方法在各方法之后运行。
在此图像中(单击图像查看大图),您可以看到项目未通过其中一个测试。testTimeout 方法花费了过长的运行时间,并且测试线程被中断,导致测试失败。它花了超过 1000 毫秒来计算出随机生成数(22991)的阶乘。.
创建单元测试之后的下一步是创建测试套件。请参见创建 JUnit 3 测试套件,了解如何以组的方式运行特定的测试,从而避免单独运行各个测试。
编写 JUnit 4 测试
在本练习中,您将为 Vectors.java 和 Utils.java 创建 JUnit 4 单元测试。 JUnit 4 测试用例与 JUnit 3 测试用例相同,但是您会发现其编写测试的语法更加简单。
您将使用 IDE 根据项目中的类来创建测试框架。第一次使用 IDE 创建测试框架时,IDE 会提示您选择 JUnit 版本。
如果您已经选择了 JUnit 3.x 作为测试缺省版本,则需要将缺省版本更改为 JUnit 4.x。要更改缺省的 JUnit 版本,右键单击“测试库”节点,选择“添加库”并将 JUnit 4 库添加到项目中。创建新测试时,在提示选择 JUnit 版本的窗口中选择 version 4.x。选择 JUnit 4.x 之后,IDE 将删除不必要的 JUnit 3.x 库。您仍然可以运行 JUnit 3 测试,但是您创建的任何新测试都将使用 JUnit 4。
为 Vectors.java 创建测试类
在本练习中,您将为 Vectors.java 创建 JUnit 测试框架。
- 右键单击 Vectors.java 并选择“工具”>“创建 JUnit 测试”。
- 在“选择 JUnit 版本”对话框中,选择“JUnit 4.x”。
选择 JUnit 4.x 之后,IDE 将删除 JUnit 3 库。如果您的项目中有 JUnit 4 库,那么您可以编写和运行 JUnit 3 及 JUnit 4 测试。
- 在“创建测试”对话框中,将测试类的名称修改为 VectorsJUnit4Test。
更改测试类的名称之后,您将看到一个关于修改名称的警告。缺省名称基于要测试的类名,并在该名称后面附加单词 Test。举例来说,对于 MyClass.java 类,测试类的缺省名称为 MyClassTest.java。 与 JUnit 3 不同,在 JUnit 4 中,测试不需要以单词 Test 结尾。通常,最好是保留缺省名称,但由于在本教程中您将在相同的包中创建所有 JUnit 4 测试,因此测试类的名称必须是惟一的。
- 取消选中“测试初始化函数”和“测试释放方法”单击“确定”。

单击确定之后,IDE 将在 sample 测试包目录中创建一个 JUnit 测试框架。
项目需要一个目录来保存测试包创建的测试。测试包目录的缺省位置为项目的根目录,但是根据项目的类型不同,您可以在项目的“属性”对话框中为目录指定不同的位置。
在编辑器中查看 VectorsJUnit3Test.java,您可以看到 IDE 生成了测试方法 testEqual 和 testScalarMultiplication。在 JUnit 4 中,各测试方法都添加了 @Test 标注。IDE 根据 Vectors.java 中的方法的名称为测试方法生成了名称,但是,不需要将 test 置于测试方法的名称之前。各生成测试方法的缺省主体是作为指导单独提供的,因此需要将它们修改为实际的测试用例。
如果您不需要生成的方法主体,可以在“创建测试”对话框中取消选中“缺省方法主体”。
IDE 还生成了以下测试类初始化函数和释放方法:
@BeforeClass public static void setUpClass() throws Exception { }
@AfterClass public static void tearDownClass() throws Exception { }
创建 JUnit 4 测试类时,IDE 会生成缺省的类初始化函数和释放方法。标注 @BeforeClass 和 @AfterClass 用于标记应在测试类之前及之后运行的方法。您可以删除这些方法,因为您在 Vectors.java 测试时不需要它们。
您可以通过在“选项”窗口中配置“JUnit”选项来配置缺省生成的方法。
为 Vectors.java 编写测试方法
在本练习中,您将修改生成的各测试方法:使用 JUnit assert 方法来测试方法,并更改测试方法的名称。在 JUnit 4 中,您在命名测试方法时拥有了更好的灵活性,因为测试方法是由 @Test 标注指示的,并且不需要将单词 test 置于测试方法之前。
- 在编辑器中打开 VectorsJUnit4Test.java。
- 修改 testScalarMultiplication 的测试框架,方法是修改方法名称、修改 println 的值并删除生成的变量。现在,测试方法应如下所示(粗体为更改部分):
@Test public void ScalarMultiplicationCheck() { System.out.println("* VectorsJUnit4Test: ScalarMultiplicationCheck()"); assertEquals(expResult, result); }
在编写测试时,不需要更改打印的输出。在本练习中,其作用是能够更加轻松地识别输出窗口中的测试结果。
- 现在,添加一些断言来测试方法。
@Test public void ScalarMultiplicationCheck() { System.out.println("* VectorsJUnit4Test: ScalarMultiplicationCheck()"); assertEquals( 0, Vectors.scalarMultiplication(new int[] { 0, 0}, new int[] { 0, 0})); assertEquals( 39, Vectors.scalarMultiplication(new int[] { 3, 4}, new int[] { 5, 6})); assertEquals(-39, Vectors.scalarMultiplication(new int[] {-3, 4}, new int[] { 5,-6})); assertEquals( 0, Vectors.scalarMultiplication(new int[] { 5, 9}, new int[] {-9, 5})); assertEquals(100, Vectors.scalarMultiplication(new int[] { 6, 8}, new int[] { 6, 8})); }
在此测试方法中,您使用了 JUnit assertEquals 方法。要使用断言,您需要提供输入变量和预期的结果。在运行被测试的方法时,要通过测试,测试方法必须根据提供的变量成功返回所有预期的结果。您应该添加足够数量的断言来涵盖各种可能的排列。
- 将 testEqual 测试方法的名称更改为 equalsCheck。
- 修改 equalsCheck 测试方法,删除生成的方法主体并添加以下 println。
System.out.println("* VectorsJUnit4Test: equalsCheck()");
现在,测试方法应如下所示:
@Test public void equalsCheck() { System.out.println("* VectorsJUnit4Test: equalsCheck()"); }
- 修改 equalsCheck 方法:添加以下断言(显示为粗体)。
public void testEqual() { System.out.println("* VectorsJUnit4Test: testEqual()"); assertTrue(Vectors.equal(new int[] {}, new int[] {})); assertTrue(Vectors.equal(new int[] {0}, new int[] {0})); assertTrue(Vectors.equal(new int[] {0, 0}, new int[] {0, 0})); assertTrue(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 0, 0})); assertTrue(Vectors.equal(new int[] {5, 6, 7}, new int[] {5, 6, 7}));
assertFalse(Vectors.equal(new int[] {}, new int[] {0})); assertFalse(Vectors.equal(new int[] {0}, new int[] {0, 0})); assertFalse(Vectors.equal(new int[] {0, 0}, new int[] {0, 0, 0})); assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 0})); assertFalse(Vectors.equal(new int[] {0, 0}, new int[] {0})); assertFalse(Vectors.equal(new int[] {0}, new int[] {}));
assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 0, 1})); assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {0, 1, 0})); assertFalse(Vectors.equal(new int[] {0, 0, 0}, new int[] {1, 0, 0})); assertFalse(Vectors.equal(new int[] {0, 0, 1}, new int[] {0, 0, 3})); }
此方法使用 JUnit assertTrue 和 assertFalse 方法来测试各种可能的结果。要通过此方法的测试,assertTrue 必须全部为 true,并且 assertFalse 必须全部为 false。
比较:为 Vectors.java 编写测试方法(JUnit 3)
为 Utils.java 创建测试类
现在,您将为Utils.java 创建 JUnit 测试方法。在上一练习中创建了测试类之后,IDE 会提示您选择 JUnit 的版本。 这次,IDE 并未提示您选择版本,因为您已经选择了 JUnit 版本,并且该版本中已经创建了所有后续 JUnit 测试。
如果您选择 JUnit 4 作为版本,那么仍然可以编写和运行 JUnit 3 测试,但是 IDE 将使用 JUnit 4 模板来生成测试框架。
- 右键单击 Utils.java 并选择“工具”>“创建 JUnit 测试”。
- 在对话框中,选中“测试初始化函数”和“测试释放方法”(如果为未选中状态)。
- 在“创建测试”对话框中,将测试类的名称修改为 UtilsJUnit4Test。单击“确定”。
单击“确定”之后,IDE 将在“测试包”>“samples”目录中创建测试文件UtilsJUnit4Test.java。可以看到,IDE 为 Utils.java 中的方法生成了 testComputeFactorial、testConcatWords 和 testNormalizeWord 测试方法。IDE 还为测试和测试类生成了初始化函数和释放方法。
为 Utils.java 编写测试方法
在本练习中,您将添加一些测试用例来演示一些常用的 JUnit 测试元素。您还将在方法中添加一个 println,因为一些方法不会在“JUnit 测试结果”窗口中打印任何输出 ,以表示它们已经运行,或表示方法已经通过测试。 通过在方法中添加 println,您可以了解方法是否已经运行以及它们运行的顺序。
测试初始化函数和释放方法
为 Utils.java 创建测试类之后,IDE 将生成标注初始化函数和释放方法。您可以为方法选择任何名称,因为没有既定的命名约定。
您不需要使用初始化函数和释放方法来测试 Utils.java,但是本教程演示了它们的运行原理。
在 JUnit 4 中,您可以使用标注来标记以下类型的初始化函数和释放方法。
- 测试类初始化函数。@BeforeClass 标注将方法标记为测试类初始化方法。测试类初始化方法只能运行一次,并且在测试类中的任何其他方法之前运行。举例来说,您不必在测试初始化函数中创建数据库连接并在各测试方法之前创建新连接,您可以在运行测试之前使用测试类初始化函数打开连接。然后,您可以使用测试类释放方法来关闭连接。
- 测试类释放方法。@AfterClass 标注将方法标记为测试类释放方法。测试类释放方法只能运行一次,并且在测试类中的任何其他方法完成之后运行。
- 测试初始化函数。@BeforeClass 标注将方法标记为测试初始化方法。测试初始化方法在测试类中的各测试用例之前运行。运行测试不需要测试初始化方法,但是,如果您需要在运行测试之前初始化一些变量,则可以使用测试初始化方法。
- 测试释放方法。@After 标注将方法标记为测试释放方法。测试释放方法在测试类中的各测试用例之后运行。运行测试不需要测试释放方法,但是,您可能需要使用释放方法来清理运行测试用例时所需的任何数据。
执行以下更改(显示为粗体),在初始化函数和释放方法中添加一个 println。
@BeforeClass public static void setUpClass() throws Exception { System.out.println("* UtilsJUnit4Test: @BeforeClass method"); }
@AfterClass public static void tearDownClass() throws Exception { System.out.println("* UtilsJUnit4Test: @AfterClass method"); }
@Before public void setUp() { System.out.println("* UtilsJUnit4Test: @Before method"); }
@After public void tearDown() { System.out.println("* UtilsJUnit4Test: @After method"); }
比较:测试初始化函数和释放方法(JUnit 3)
运行测试类时,您添加的 println 文本将显示在“JUnit 测试结果”窗口的输出窗格中。如果您没有添加 println,则不会在输出中显示初始化函数和释放方法已经运行。
在测试中使用简单的断言
这个简单的测试用例将测试 concatWords 方法。 您没有使用生成的 testConcatWords 测试方法,而是使用新的 helloWorldCheck 测试方法,该方法使用单个简单的断言来测试方法是否正确地连接了字符串。测试用例中的 assertEquals 将使用 assertEquals(EXPECTED_RESULT, ACTUAL_RESULT) 语法来测试预期结果是否等于实际结果。在本例中,如果 concatWords 方法的输入为 "Hello"、", "、"world" 和 "!",则预期结果应该等于 "Hello, world!"。
- 删除生成的测试方法 testConcatWords。
- 添加以下 helloWorldCheck 方法来测试 Utils.concatWords。
public void testHelloWorld() { assertEquals("Hello, world!", Utils.concatWords("Hello", ", ", "world", "!")); }
- 添加一个 println 语句,用于在“JUnit 测试结果”窗口中显示关于测试的文本。
@Test public void helloWorldCheck() { System.out.println("* UtilsJUnit4Test: test method 1 - helloWorldCheck()"); assertEquals("Hello, world!", Utils.concatWords("Hello", ", ", "world", "!"));
比较:在测试中使用简单的断言(JUnit 3)
在测试中使用超时
此测试演示如何检查方法的完成是否花费了过长的时间。如果方法花费了过长的时间,则测试线程将中断并导致测试失败。您可以在测试中指定时间限制。
测试方法将调用 Utils.java 中的 computeFactorial 方法。您可以假定 computeFactorial 方法是正确的,但是在本例中,您希望测试计算是否是在 1000 毫秒之内完成的。其作用是在 1000 毫秒之好中断测试线程。如果线程被中断,则测试方法将抛出一个 TimeoutException。
- 删除生成的测试方法 testComputeFactorial。
- 添加 testWithTimeout 方法,该方法用于计算随机生成数的阶乘。
@Test public void testWithTimeout() { final int factorialOf = 1 + (int) (30000 * Math.random()); System.out.println("computing " + factorialOf + '!'); System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); }
- 添加以下代码(显示为粗体),用于设置超时并在方法执行时间过长时中断线程。
@Test(timeout=1000) public void testWithTimeout() { final int factorialOf = 1 + (int) (30000 * Math.random());
可以看到,超时被设置为 1000 毫秒。
- 添加以下 println(显示为粗体),用于在“JUnit 测试结果”窗口中输出关于测试的文本。
@Test(timeout=1000) public void testWithTimeout() { System.out.println("* UtilsJUnit4Test: test method 2 - testWithTimeout()"); final int factorialOf = 1 + (int) (30000 * Math.random()); System.out.println("computing " + factorialOf + '!');
比较:在测试中使用超时(JUnit 3)
针对预期异常的测试
此测试演示如何针对预期异常进行测试。如果未抛出指定的预期异常,则会导致方法失败。在本例中,您将测试当输入变量为负数时(-5),computeFactorial 方法是否会抛出 IllegalArgumentException。
- 添加以下 testExpectedException 方法,它将以输入 -5 调用 computeFactorial 方法。
@Test public void checkExpectedException() { final int factorialOf = -5; System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); }
- 在 @Test 标注中添加以下属性(显示为粗体),指定测试应该抛出 IllegalArgumentException。
@Test(expected=IllegalArgumentException.class) public void checkExpectedException() { final int factorialOf = -5; System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); }
- 添加以下 println(显示为粗体),用于在“JUnit 测试结果”窗口中输出关于测试的文本。
@Test (expected=IllegalArgumentException.class) public void checkExpectedException() { System.out.println("* UtilsJUnit4Test: test method 3 - checkExpectedException()"); final int factorialOf = -5; System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf)); }
比较:针对预期异常的测试(JUnit 3)
禁用测试
此测试演示如何临时禁用测试方。在 JUnit 4 中,您只需通过添加 @Ignore 标注来禁用测试。
- 删除生成的测试方法 testNormalizeWord。
- 在测试类中添加以下测试方法:
@Test public void temporarilyDisabledTest() throws Exception { System.out.println("* UtilsJUnit4Test: test method 4 - checkExpectedException()"); assertEquals("Malm\u00f6", Utils.normalizeWord("Malmo\u0308")); }
测试方法 temporarilyDisabledTest 将在您运行测试类时运行。
- 在 @Test 上方添加 @Ignore 标注(显示为粗体)来禁用测试。
@Ignore @Test public void temporarilyDisabledTest() throws Exception { System.out.println("* UtilsJUnit4Test: test method 4 - checkExpectedException()"); assertEquals("Malm\u00f6", Utils.normalizeWord("Malmo\u0308")); }
- 修复导入,以导入 org.junit.Ignore。
比较:禁用测试(JUnit 3)
现在,您已经编写了测试。接下来,您可以运行测试并在“JUnit 测试结果”窗口中查看测试输出。
运行测试
您可以对整个应用程序或单独的文件运行 JUnit 测试,并在 IDE 中查看结果。要运行项目的所有单元测试,最简单的方法是从主菜单中选择“运行”>“测试 <PROJECT_NAME>”。如果您选择此方法,则 IDE 将运行“测试包”中的所有测试类。要运行单独的测试类,右键单击“测试包”节点下的测试类并选择“运行文件”。
- 在“项目”窗口中,右键单击 UtilsJUnit4Test.java。
- 选择“运行文件”。
在运行 UtilsJUnit4Test.java 时,IDE 仅运行测试类中的测试。如果类通过了所有测试,那么您将在“JUnit 测试结果”窗口中看到与下图相似的结果。
在该图中(单击图像查看大图),您可以看到 IDE 对 Utils.java 运行了 JUnit 测试,并且类通过了所有测试。左侧窗格显示各测试方法的结果,右侧窗格显示测试输出。如果您查看输出,您可以看到测试运行的顺序。添加到各测试方法中的 println 在输出窗口中输出测试的名称。从 UtilsJUnit4Test 中可以看到,使用 @BeforeClass 进行标注的测试类初始化方法运行于任何其他方法之前,并且只运行一次。使用 @AfterClass 进行标注的测试类初始化方法在类中的所有其他方法之后运行。使用 @Before 进行标注的测试初始化方法在各测试方法之前运行。
创建单元测试之后的下一步是创建测试套件。请参见创建 JUnit 4 测试套件,了解如何以组的方式运行特定的测试,从而避免单独运行各个测试。
创建测试套件
为项目创建测试之后,您最后将得到许多测试类。虽然您可以单独运行测试类,也可以运行项目中的所有测试,但在许多情况下,您希望运行测试的子集或按特定的顺序运行测试。您可以通过创建一个或多个测试套件来实现此目的。举例来说,您可以创建测试套件来测试代码的具体方面或具体的条件。
从根本上讲,测试套件是一个类,其中包含对指定测试用例(例如,特定测试类、测试类中的测试方法以及其他测试套件)进行调用的方法。测试套件可以作为测试类的一部分包括在其中,但最佳实践建议单独创建测试套件类。
您可以手动为项目创建 JUnit 3 和 JUnit 4 测试套件,也可以通过 IDE 来生成套件。使用 IDE 生成测试套件之后,缺省情况下,IDE 将生成代码来调用与测试套件相同的包中的所有测试类。创建测试套件之后,您可以对类进行修改,指定希望作为该套件一部分运行的测试。
创建 JUnit 3 测试套件
如果您选择 JUnit 3 作为测试的版本,则 IDE 可以根据测试包中的测试类来生成 JUnit 3 测试套件。在 JUnit 3 中,您可以指定要包含在测试套件中的测试类,方法是创建一个 TestSuite 实例并调用各测试的 addTest 方法。
- 在“项目”窗口中右键单击项目节点,然后选择“新建”>“其他”以打开“新建文件”向导。
- 选择“JUnit”类别和“测试套件”。单击“下一步”。
- 键入 JUnit3TestSuite 作为文件名称。
- 选择 sample 包,在测试包文件夹的 sample 文件夹中创建测试套件。
- 取消选中“测试初始化函数”和“测试释放方法”单击“完成”。
单击“完成”后, IDE 将在 sample 包中创建测试套件类,并在编辑器中打开类。如果 sample 包包含测试类 VectorsJUnit3Test.java 和 UtilsJUnit3Test.java,则测试套件将包含以下代码。
public JUnit3TestSuite(String testName) { super(testName); }
public static Test suite() { TestSuite suite = new TestSuite("JUnit3TestSuite"); suite.addTest(new TestSuite(sample.VectorsJUnit3Test.class)); suite.addTest(new TestSuite(sample.UtilsJUnit3Test.class)); return suite; }
创建 JUnit 4 测试套件
如果您选择 JUnit 4 作为测试的版本,则 IDE 可以生成 JUnit 4 测试套件。JUnit 4 是向后兼容的,因此您可以运行包含 JUnit 4 和 JUnit 3 测试的 JUnit 4 测试套件。在 JUnit 4 测试套件中,您可以以 @Suite 标注的值的形式指定要包括的测试类。
要让 JUnit 3 作为 JUnit 4 的一部分来运行,需要 JUnit 4.4 或更高版本。
- 在“项目”窗口中右键单击项目节点,然后选择“新建”>“其他”以打开“新建文件”向导。
- 选择“JUnit”类别和“测试套件”。单击“下一步”。
- 键入 JUnit4TestSuite 作为文件名称。
- 选择 sample 包,在测试包文件夹的 sample 文件夹中创建测试套件。
- 取消选中“测试初始化函数”和“测试释放方法”单击“完成”。
单击“完成”后, IDE 将在 sample 包中创建测试套件类,并在编辑器中打开类。测试套件包含以下代码。
@RunWith(Suite.class) @Suite.SuiteClasses(value={UtilsJUnit4Test.class, VectorsJUnit4Test.class}) public class JUnit4TestSuite { }
运行测试套件时,IDE 将按照测试类 UtilsJUnit4Test 和 VectorsJUnit4Test 的列出顺序来运行它们。
运行测试套件
运行测试套件与运行任何单独测试类的方法相同。
- 在“项目”窗口中展开“测试包”节点。
- 右键单击测试套件并选择“运行文件”。
运行测试套件之后,IDE 将按照套件中的测试的列出顺序来运行它们。JUnit 测试结果显示在“JUnit 测试结果”窗口中。
在该图中(单击图像查看大图),您可以看到 JUnit 3 测试套件的测试结果。测试套件将以单独测试的形式来运行 UtilsJUnit3Test 和 VectorsJUnit3Test 测试类,并在左侧窗格中显示各测试的结果。 单独运行测试时,右侧窗格中的输出与左侧相同。
在该图中(单击图像查看大图),您可以看到 JUnit 4 测试套件的测试结果。测试套件将以单独测试的形式来运行 UtilsJUnit4Test 和 VectorsJUnit4Test 测试类,并在左侧窗格中显示各测试的结果。 单独运行测试时,右侧窗格中的输出与左侧相同。
在该图中(单击图像查看大图),您可以看到混合测试套件的测试结果。该测试套件包括 JUnit 4 测试套件并一个 JUnit 3 测试类。测试套件将以单独测试的形式来运行 UtilsJUnit3Test.java 和 JUnit4TestSuite.java 测试类,并在左侧窗格中显示各测试的结果。 单独运行测试时,右侧窗格中的输出与左侧相同。
本教程是在 NetBeans IDE 中创建 JUnit 单元测试和测试套件的基本介绍。通常,测试代码能帮助确保对代码中所做的小更改不会中断应用程序。JUnit 等自动化测试工具简化了测试的流程,并且经常性的测试能帮助及时捕获代码错误。
另请参见
有关使用 NetBeans IDE 开发 Java 应用程序的更多信息,请参见以下资源:
|
|