JUnit介绍,JUnit是什么?
JJUnit是用于编写和运行可重复的自动化测试的开源测试框架, 这样可以保证我们的代码按预期工作。JUnit可广泛用于工业和作为支架(从命令行)或IDE(如Eclipse)内单独的Java程序。
JUnit提供:
- 断言测试预期结果。
- 测试功能共享通用的测试数据。
- 测试套件轻松地组织和运行测试。
- 图形和文本测试运行。
JUnit用于测试:
- 整个对象
- 对象的一部分 - 交互的方法或一些方法
- 几个对象之间的互动(交互)
JUnit的特点
-
JUnit是用于编写和运行测试的开源框架。
-
提供了注释,以确定测试方法。
-
提供断言测试预期结果。
-
提供了测试运行的运行测试。
-
JUnit测试让您可以更快地编写代码,提高质量
-
JUnit是优雅简洁。它是不那么复杂以及不需要花费太多的时间。
-
JUnit测试可以自动运行,检查自己的结果,并提供即时反馈。没有必要通过测试结果报告来手动梳理。
-
JUnit测试可以组织成测试套件包含测试案例,甚至其他测试套件。
-
Junit显示测试进度的,如果测试是没有问题条形是绿色的,测试失败则会变成红色。
JUnit的版本
2016/2 5.0内测版本
2017/11 5.0第一个正式版本
当前最新5.3.2
junit4已经发布了10多年 最新版本为4.12
JUnit注解
在本节中,我们将提到支持在JUnit4基本注释,下表列出了这些注释的概括:
注解 | 描述 |
---|---|
@Test public void method() | 测试注释指示该公共无效方法它所附着可以作为一个测试用例。 |
@Before public void method() | Before注释表示,该方法必须在类中的每个测试之前执行,以便执行测试某些必要的先决条件。 |
@BeforeClass public static void method() | BeforeClass注释指出这是附着在静态方法必须执行一次并在类的所有测试之前。发生这种情况时一般是测试计算共享配置方法(如连接到数据库)。 |
@After public void method() | After 注释指示,该方法在执行每项测试后执行(如执行每一个测试后重置某些变量,删除临时变量等) |
@AfterClass public static void method() | 当需要执行所有的测试在JUnit测试用例类后执行,AfterClass注解可以使用以清理建立方法,(从数据库如断开连接)。注意:附有此批注(类似于BeforeClass)的方法必须定义为静态。 |
@Ignore public static void method() | 当想暂时禁用特定的测试执行可以使用忽略注释。每个被注解为@Ignore的方法将不被执行。 |
让我们看看一个测试类,在上面提到的一些注解的一个例子。
AnnotationsTest.java
package com.yiibai.junit; import static org.junit.Assert.*; import java.util.*; import org.junit.*; public class AnnotationsTest { private ArrayList testList; @BeforeClass public static void onceExecutedBeforeAll() { System.out.println("@BeforeClass: onceExecutedBeforeAll"); } @Before public void executedBeforeEach() { testList = new ArrayList(); System.out.println("@Before: executedBeforeEach"); } @AfterClass public static void onceExecutedAfterAll() { System.out.println("@AfterClass: onceExecutedAfterAll"); } @After public void executedAfterEach() { testList.clear(); System.out.println("@After: executedAfterEach"); } @Test public void EmptyCollection() { assertTrue(testList.isEmpty()); System.out.println("@Test: EmptyArrayList"); } @Test public void OneItemCollection() { testList.add("oneItem"); assertEquals(1, testList.size()); System.out.println("@Test: OneItemArrayList"); } @Ignore public void executionIgnored() { System.out.println("@Ignore: This execution is ignored"); } }
如果我们运行上面的测试,控制台输出将是以下几点:
@BeforeClass: onceExecutedBeforeAll @Before: executedBeforeEach @Test: EmptyArrayList @After: executedAfterEach @Before: executedBeforeEach @Test: OneItemArrayList @After: executedAfterEach @AfterClass: onceExecutedAfterAll
JUnit断言
在本节中,我们将介绍一些断言方法。所有这些方法都受到 Assert 类扩展了java.lang.Object类并为它们提供编写测试,以便检测故障。下表中有一种最常用的断言方法的更详细的解释。
断言 | 描述 |
---|---|
void assertEquals([String message], expected value, actual value) | 断言两个值相等。值可能是类型有 int, short, long, byte, char or java.lang.Object. 第一个参数是一个可选的字符串消息 |
void assertTrue([String message], boolean condition) | 断言一个条件为真 |
void assertFalse([String message],boolean condition) | 断言一个条件为假 |
void assertNotNull([String message], java.lang.Object object) | 断言一个对象不为空(null) |
void assertNull([String message], java.lang.Object object) | 断言一个对象为空(null) |
void assertSame([String message], java.lang.Object expected, java.lang.Object actual) | 断言,两个对象引用相同的对象 |
void assertNotSame([String message], java.lang.Object unexpected, java.lang.Object actual) | 断言,两个对象不是引用同一个对象 |
void assertArrayEquals([String message], expectedArray, resultArray) | 断言预期数组和结果数组相等。数组的类型可能是 int, long, short, char, byte or java.lang.Object. |
让我们看的一些前述断言的一个例子。
AssertionsTest.java
package com.yiibai.junit; import static org.junit.Assert.*; import org.junit.Test; public class AssertionsTest { @Test public void test() { String obj1 = "junit"; String obj2 = "junit"; String obj3 = "test"; String obj4 = "test"; String obj5 = null; int var1 = 1; int var2 = 2; int[] arithmetic1 = { 1, 2, 3 }; int[] arithmetic2 = { 1, 2, 3 }; assertEquals(obj1, obj2); assertSame(obj3, obj4); assertNotSame(obj2, obj4); assertNotNull(obj1); assertNull(obj5); assertTrue(var1 var2); assertArrayEquals(arithmetic1, arithmetic2); } }
在以上类中我们可以看到,这些断言方法是可以工作的。
- assertEquals() 如果比较的两个对象是相等的,此方法将正常返回;否则失败显示在JUnit的窗口测试将中止。
- assertSame() 和 assertNotSame() 方法测试两个对象引用指向完全相同的对象。
- assertNull() 和 assertNotNull() 方法测试一个变量是否为空或不为空(null)。
- assertTrue() 和 assertFalse() 方法测试if条件或变量是true还是false。
- assertArrayEquals() 将比较两个数组,如果它们相等,则该方法将继续进行不会发出错误。否则失败将显示在JUnit窗口和中止测试。
使用@Ignore注解
断续上一节的例子,了解如何使用@Ignore注解。在测试类FirstDayAtSchoolTest中,我们将添加@Ignore注解到testAddPencils()方法。以这种方式,我们期望这个测试方法将被忽略,不被执行。
package com.yiibai.junit; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; public class FirstDayAtSchoolTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag1 = { "Books", "Notebooks", "Pens" }; String[] bag2 = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag1, school.prepareMyBag()); } @Ignore @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag2, school.addPencils()); } }
事实上,这输出会发生什么:
Inside testPrepareMyBag() My school bag contains: [Books, Notebooks, Pens]
现在,我们将从testAddPencils()方法去除@Ignore注解,修改为注释整个类来代替。
package com.yiibai.junit; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; @Ignore public class FirstDayAtSchoolTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag1 = { "Books", "Notebooks", "Pens" }; String[] bag2 = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag1, school.prepareMyBag()); } @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag2, school.addPencils()); } }
在这个测试类将不会执行,因此不显示在控制台和JUnit视图输出结果:
创建套件测试
在本节中,我们将学习如何创建套件测试。测试套件是一些测试不同类用例,可以使用@RunWith和@Suite注解运行所有东西在一起。如果有很多测试类,想让它们都运行在同一时间,而不是单一地运行每个测试,这是非常有用的。
当一个类被注解为@RunWith, JUnit 将调用被在其中注解,以便运行测试类,而不使用内置的 JUnit 运行方法。
基于前面的章节中的类,我们可以创建两个测试类。一个类将测试公共方法 prepareMyBag()和其他测试类将测试方法 addPencils()。因此,我们最终将有以下两个类:
PrepareMyBagTest.java
package com.yiibai.junit; import org.junit.Test; import static org.junit.Assert.*; public class PrepareMyBagTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag = { "Books", "Notebooks", "Pens" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag, school.prepareMyBag()); } }
AddPencilsTest.java
package com.yiibai.junit; import org.junit.Test; import static org.junit.Assert.*; public class AddPencilsTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag, school.addPencils()); } }
现在,我们将创建一个测试套件,以便运行上面的类在一起。用鼠标右键单击 test 源文件夹,并创建一个新的名为SuiteTest.java 的Java类,使用下面的代码:
SuiteTest.java
package com.yiibai.junit; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ PrepareMyBagTest.class, AddPencilsTest.class }) public class SuitTest { }
使用@ Suite.SuiteClasses注解,你可以定义测试类,将被列入执行。
所以,如果用鼠标右键单击测试套件并选择 Run As -> JUnit Test, 两个测试类与已经在@ Suite.SuiteClasses注解定义的顺序执行。
创建参数化测试
在本节中,我们将学习如何创建参数测试。为此,我们将使用前面章节中提供的一个公共方法添加整数。因此,这是要进行的测试类。
但是,一个测试类也可以被看作是一个参数化测试类?当然,但它要满足下列所有要求:
- 该类被注解为 @RunWith(Parameterized.class).
如前一节中所说明的, @RunWith 注解让JUnit来调用其中的注释来运行测试类,代替使用内置的JUnit运行器,Parameterized 是一个在JUnit内的运行器将运行相同的测试用例组在不同的输入。 - 这个类有一个构造函数,存储测试数据。
- 这个类有一个静态方法生成并返回测试数据,并注明@Parameters注解。
- 这个类有一个测试,它需要注解@Test到方法。
现在,我们将创建一个名为 CalculateTest.java 的一个新的测试类, 遵循上述指导原则。这个类的源代码如下。
CalculateTest.java
package com.yiibai.junit; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class CalculateTest { private int expected; private int first; private int second; public CalculateTest(int expectedResult, int firstNumber, int secondNumber) { this.expected = expectedResult; this.first = firstNumber; this.second = secondNumber; } @Parameters public static Collection addedNumbers() { return Arrays.asList(new Integer[][] { { 3, 1, 2 }, { 5, 2, 3 }, { 7, 3, 4 }, { 9, 4, 5 }, }); } @Test public void sum() { Calculate add = new Calculate(); System.out.println("Addition with parameters : " + first + " and " + second); assertEquals(expected, add.sum(first, second)); } }
正如我们观察上面的类,它满足上述所有要求。addedNumbers方法使用注释@Parameters返回数组的集合。每个数组包括每个测试执行输入/输出数字。每个数组中的元素数必须相同以在构造参数的数目。所以,在这种特定的情况下,每个数组包括三个元素,即表示要加入的数字两个元素和一个元素结果。
如果我们运行 CalculateTest 测试用例,控制台输出如下:
Addition with parameters : 1 and 2 Adding values: 1 + 2 Addition with parameters : 2 and 3 Adding values: 2 + 3 Addition with parameters : 3 and 4 Adding values: 3 + 4 Addition with parameters : 4 and 5 Adding values: 4 + 5
正如我们在输出中看到,测试用例被执行4次,这是在方法的输入注解为@Parameters的数量。
JUnit规则
在本节中,我们学习和了解JUnit中叫做规则的新功能,它允许非常灵活在测试类重新定义每个测试方法的行为。为了这个目的,@Rule注解被使用来标出测试类的公共字段。这些字段类型为MethodRule,这是测试方法如何运行并报告。多个MethodRules可以应用到一个测试方法。MethodRule接口有很多的实现,如ErrorCollector在发现了第一个问题之后,也允许继续执行一个测试,ExpectedException 允许在测试规范预期的异常类型和消息,TestName 使得目前的测试名称在测试方法内部可用,以及其他许多。除了那些已经定义的规则,开发人员可以创建自己的自定义规则,并使用来测试自己用例。
下面我们介绍方法,可以使用一个名为TestName,在我们自己现有的测试规则。 TestName被调用在测试开始时。
NameRuleTest.java
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.*; import org.junit.rules.TestName; public class NameRuleTest { @Rule public TestName name = new TestName(); @Test public void testA() { System.out.println(name.getMethodName()); assertEquals("testA", name.getMethodName()); } @Test public void testB() { System.out.println(name.getMethodName()); assertEquals("testB", name.getMethodName()); } }
我们可以看到,@Rule注解标记的类型是MethodRule的公共字段名,具体而言是TestName类型。 然后,可以在我们的测试中,这名称字段使用和查找,例如测试方法的名称,这种特定情况。
在命令行中运行JUnit测试
可以在Eclipse之外运行JUnit测试,使用org.junit.runner.JUnitCore类。 这个类提供了runClasses()方法,它允许运行一个或多个测试类。runClasses()方法返回类型是org.junit.runner.Result对象类型。 这个对象可以被用来收集关于测试信息。此外,如果有一个失败的测试,可以用org.junit.runner.notification.Failure对象保存失败测试的描述。
下面的步骤显示了如何在Eclipse之外(命令行下)运行测试。
用下面的代码创建一个新的Java类为JunitRunner.java:
JunitRunner.java
package com.yiibai.junit; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class JunitRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(AssertionsTest.class); for (Failure fail : result.getFailures()) { System.out.println(fail.toString()); } if (result.wasSuccessful()) { System.out.println("All tests finished successfully..."); } } }
作为一个例子,我们选择运行AssertionsTest测试类。
1 | C:\Users\yiibai\eclipse_workspace\JUnitGuide\test\com\yiibai\junit>javac -classpath "C:\Users\yiibai\Downloads\junit-4.11.jar";"C:\Users\yiibai\Downloads\hamcrest-core-1.3.jar"; AssertionsTest.java JunitRunner.java |
注:这里指定包含相关 junit 的相关 jar 包。
1 | C:\Users\yiibai\eclipse_workspace\JUnitGuide\test\com\yiibai\junit>java -classpath "C:\Users\yiibai\Downloads\junit-4.11.jar";"C:\Users\yiibai\Downloads\hamcrest-core-1.3.jar"; JunitRunner |
- 打开命令提示符,然后往下移目录,找到两个类所在的目录。
- 编译测试类和运行类。
- 现在运行 JunitRunner.
这里是结果输出:
All tests finished successfully...