一、单元测试的好处

  单元测试可以帮助我们验证程序的逻辑是否正确、可以降低bug修复的成本、更有利于代码重构等等。所以,我们在写代码的时候,尽量保证单元测试的覆盖率。能力好的可以先写测试用例,再写功能代码(测试先行)。

二、使用JUnit

  1、JUnit框架:JUnit是一个托管在Github上的开源项目,是Java程序员使用率最高的测试框架,使用@Test注释来标识指定测试的方法。

  2、怎么在JUnit中进行测试呢(以maven项目为例):首先在pom中引入JUnit的依赖,然后在src/test/java中建立相关测试类,编写测试用例方法,在方法上添加@Test,并在方法内使用合适的assert进行判断,运行即可。实例如下:

public class MyTest001 {
@Test
public void test001(){
Assert.assertEquals(1,1);
}
}


  3、命名约定:

    ①在/src/test/java中建立与/src/main/java相对应的测试类,以Test为后缀。

    ②测试名称应该见名知意,一般使用多should、when、then等单词。

  4、JUnit4中常用的注释

@Test

将方法标识为测试方法,还可以有expected和timeout两个属性

@Test(expected = Exception.class)

如果方法不抛出指定的异常,则失败

@Test(timeout = 100)

如果方法花费的时间超过100毫秒,则会失败

@Before

在测试方法执行之前,先执行被@Before标记的方法,用于准备测试环境,如初始化类等。

@After

在测试方法执行之后执行,用于清理测试环境,如删除临时数据等。

@BeforeClass

在所有测试开始之前执行一次,用于执行时间密集型活动,如数据库连接。被@BeforeClass标记的方法需要是static修饰

@AfterClass

在所有测试完成后执行一次。用于执行清理活动,如断开与数据库的连接。被@AfterClass标记的方法需要是static修饰

@Ignore

标记应禁用测试。当底层代码已更改且测试用例尚未调整时,非常有用。最好描述禁用原因。

代码示例:

public class MyTest001 {

@BeforeClass
public static void beforeClass(){
System.out.println("MyTest001.beforeClass ...");
}

@Before
public void before(){
System.out.println("MyTest001.before...");
}

@Test
public void test001(){
System.out.println("MyTest001.test001...");
Assert.assertEquals(1,1);
}

@Test
@Ignore("测试用例没有修改...")
public void test002(){
System.out.println("MyTest001.test002...");
Assert.assertEquals(1,1);
}

@After
public void after(){
System.out.println("MyTest001.after...");
}

@AfterClass
public static void afterClass(){
System.out.println("MyTest001.afterClass ...");
}

}


 运行结果如下:

    JUnit4单元测试_方法名

  5、JUnit4断言

    JUnit通过Assert类提供的静态方法来测试某些条件。这些断言语句通常是以assert开头。可以指定错误信息、预期结果和实际结果。断言方法将测试返回的实际值和预期值进行比较。如果比较失败,将抛出java.lang.AssertionError异常。

    常用断言如下,[message]参数可选:

fail([String message])

让测试方法失败。

assertTrue([String message,] boolean condition)

检查布尔条件是否为true。对应方法名称assertFalse


assertEquals([String message,] 多种类型 unexpected,
多种类型 actual)


测试两个值是否相同。对应方法名称assertNotEquals。


assertEquals([String message,] double/float expected,
double/float actual, double/float delta)


测试float或double值是否匹配。容差是必须相同的小数位数。对应方法名称assertNotEquals。

assertNull([String message,] Object object)

检查对象是否为空。对应方法名称assertNotNull。

assertSame([String message,]Object expected, Object actual)

检查两个变量是否引用同一个对象。对应方法名称assertNotSame


assertArrayEquals([String message,] 各种类型[] expecteds,
各种类型[] actuals)


检查数组内容是否相同。


assertArrayEquals([String message,] double/float[] expecteds,
double/float[] actuals, double/float delta)


检查double/float数组内容是否相同,指定容差范围。


assertThat([String message, ]T actual, Matcher matcher)


actual为需要测试的变量,matcher为使用Hamcrest的匹配符来表达变量actual期望值的声明。(其实内部调用的是org.hamcrest.MatcherAssert.assertThat)


  6、测试类中测试方法的执行顺序

    测试类中的测试方法,也是可以指定按方法名称的字典顺序执行的,在测试类上添加@FixMethodOrder注解,有三个值可选分别是:MethodSorters.DEFAULT:默认执行顺序,由方法名hashcode值来决定,如果hash值大小一致,则按名字的字典顺序确定;MethodSorters.NAME_ASCENDING:按方法名称的字典顺序;MethodSorters.JVM:按JVM返回的方法名的顺序执行。示例代码如下:

/**
* @FixMethodOrder指定测试方法执行顺序
* MethodSorters.DEFAULT:默认执行顺序,由方法名hashcode值来决定,如果hash值大小一致,则按名字的字典顺序确定。
* MethodSorters.NAME_ASCENDING:按方法名称的字典顺序。
* MethodSorters.JVM:按JVM返回的方法名的顺序执行。
*/
@FixMethodOrder(MethodSorters.JVM)
public class MyTest003 {

@Test
public void testCThird(){
System.out.println("MyTest003.testCThird");
}

@Test
public void testAFirst(){
System.out.println("MyTest003.testAFirst");
}

@Test
public void testBSecond(){
System.out.println("MyTest003.testBSecond");
}

}


运行结果:MethodSorters.DEFAULT        JUnit4单元测试_测试方法_02

     MethodSorters.NAME_ASCENDING   JUnit4单元测试_方法名_03

     MethodSorters.JVM          JUnit4单元测试_方法名_04

  7、JUnit测试套件

    我们可以将几个测试类组合到一个测试套件中,根据填写的顺序运行测试类。

/**
* 使用@Suite.SuiteClasses可以将多个测试类放到一起进行测试,也可以包含其他被@Suite.SuiteClasses标记的类
* 以指定的顺序运行套件中的所有测试类。
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({MyTest001.class,MyTest002.class,MyTest003.class})
public class AllTests {
}


  8、分类测试

    我们还可以定义测试类别,并根据注释包含或排除它们。

public interface FastTests {
/* 类别标记 */
}

public interface SlowTests {
/* 类别标记 */
}

public class A {

@Test
public void a() {
fail();
}

/**
* 该方法属于SlowTests类别
*/
@Test
@Category(SlowTests.class)
public void b() {
}

}

/**
* 该测试类属于SlowTests、FastTests两种类别
*/
@Category({SlowTests.class, FastTests.class})
public class B {

@Test
public void c() {
}
}

/**
* 分类别运行测试用例
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class) //指定包含的类别
@Categories.ExcludeCategory(FastTests.class) //指定排除的类别
@Suite.SuiteClasses( { A.class, B.class }) // 类别是一种套件
public class SlowTestSuite {
// 不打开注释@Categories.ExcludeCategory,将会运行 A.b and B.c, 不会运行 A.a
// 打开注释@Categories.ExcludeCategory,将会运行 A.b
}


  8、JUnit参数化测试:​​https://github.com/Pragmatists/JUnitParams​

  9、JUnit规则:​​https://github.com/junit-team/junit4/wiki/Rules​