JUnit4单元测试

一、单元测试的好处

  单元测试可以帮助我们验证程序的逻辑是否正确、可以降低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 ...");
    }

}

 运行结果如下:

    

  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        

     MethodSorters.NAME_ASCENDING   

     MethodSorters.JVM          

  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

原文地址:https://www.cnblogs.com/caofanqi/p/10820425.html