Junit

1. 概念

JUnit是一个Java语言的单元测试框架。
单元测试:单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

2. Junit特性

测试工具

  • 测试套件
  • 测试运行器
  • 测试分类

3. API

注解

序号

注释

描述

1

@Test

依附在Junit的public void方法上,作为一个测试用例。

2

@Before

表示必须在每一个测试之前执行,以便执行测试某些必要的先决条件。

3

@BeforeClass

依附在静态方法上,在类的所有测之前必须执行。一般是测试计算共享配置方法(连接到数据库)。

4

@After

表示在每一个测试后执行,如执行每一个测试后重置或删除某些变量。

5

@AfterClass

在所有测试用例后执行,可用于清理建立方法,如断开数据库连接。和@BeforeClass一样附着的方法必须是静态。

6

@Ignore

当想暂时禁用特定的测试执行的时候可以使用,被注解为@Ignore的方法将不被执行。

  • 代码示例:
import org.junit.*;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestAnnotation {

    //指出这是附着在静态方法必须执行一次并在类的所有测试之前。
    // 发生这种情况时一般是测试计算共享配置方法(如连接到数据库)
    @BeforeClass
    public static void beforeClass(){
        System.out.println("execute beforeClass");
    }

    //当需要执行所有的测试在JUnit测试用例类后执行,AfterClass注解可以使用以清理建立方法,
    // (从数据库如断开连接)。注意:附有此批注(类似于BeforeClass)的方法必须定义为静态。
    @AfterClass
    public static void afterClass(){
        System.out.println("execute afterClass");
    }


    //Before注释表示,该方法必须在类中的每个测试之前执行,以便执行测试某些必要的先决条件。
    @Before
    public void before(){
        System.out.println("execute before");
    }

    //After 注释指示,该方法在执行每项测试后执行(如执行每一个测试后重置某些变量,删除临时变量等)
    @After
    public void after(){
        System.out.println("execute after");
    }

    //测试注释指示该公共无效方法它所附着可以作为一个测试用例。
    @Test
    public void test(){
        System.out.println("execute test");
    }

    @Test
    public void test1(){
        System.out.println("execute test1");
    }

    //当想暂时禁用特定的测试执行可以使用忽略注释。每个被注解为@Ignore的方法将不被执行。
    @Ignore
    public void ignoreTest(){
        System.out.println();
    }
}
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestRunner2 {
    public static void main(String[] args) {
        Result result= JUnitCore.runClasses(TestAnnotation.class);
        for (Failure failure:result.getFailures()){
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}
  • 运行结果:
execute beforeClass
execute before
execute test
execute after
execute before
execute test1
execute after
execute afterClass
true
  • junit的执行过程
  • beforeClass首先执行,只执行一次
  • afterClass最后执行,只执行一次
  • before在每次测试之前执行
  • after在每次测试之后执行
  • 每个test在after和before之间执行

断言

序号

方法

描述

1

assertEquals(boolean expected, boolean actual)

检查两个变量或者等式是否平衡

2

assertTrue(boolean expected, boolean actual)

检查条件是否为真

3

assertFalse(boolean condition)

检查条件是否为假

4

assertNotNull(Object object)

检查变量是否不为空

5

assertNull(Object object)

检查变量是否为空

6

assertSame(Object expected, Object actual)

检查两个引用是否指向同一个对象

7

assertNotSame(Object expected, Object actual)

检查两个引用是否不指向同一个对象

8

assertArrayEquals(expectedArray, resultArray)

检查两个数组是否相等

  • 代码示例
import org.junit.Test;

import static org.junit.Assert.*;

public class TestAssertions {

    @Test
    public void testAssertions(){
        String str1=new String("abc");
        String str2=new String("abc");
        String str3=null;
        String str4="abc";
        String str5="abc";
        int val1=5;
        int val2=6;
        String[] expectedArray={"one","two","three"};
        String[] resultArray={"one","two","three"};

        assertEquals(str1,str2);

        assertTrue(val1<val2);

        assertFalse(val1>val2);

        assertNotNull(str1);

        assertNull(str3);

        assertSame(str4,str5);

        assertNotSame(str1,str3);

        assertArrayEquals(expectedArray,resultArray);

    }
}

4. 执行测试

测试用例通过JUnitCore类来执行,它是Junit运行测试的外观类。对于只有一次的测试运行,可以使用静态方法:runClasses(Class[])

5. 套件测试

意思就是捆绑几个单元测试用例并一起执行。@RunWith@Suite可一起用于运行套件测试

  • 代码示例
/**
* Created by Administrator on 2017/11/1 0001.
* 被测试类
*/
public class MessageUtil {

    private String message;

    public MessageUtil(String message){
        this.message=message;
    }

    public String printMessage(){
        System.out.println(message);
        return message;
    }

    public String salutationMessage(){
        message="Hi!"+message;
        System.out.println(message);
        return message;
    }
}
import org.junit.Assert;
import org.junit.Test;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestJunit {

    String message="Robert";
    MessageUtil messageUtil=new MessageUtil(message);

    @Test
    public void testPrintMessage(){
//        message="New Word";
        System.out.println("Inside testPrintMessage()");
        Assert.assertEquals(message,messageUtil.printMessage());
    }
}
import org.junit.Assert;
import org.junit.Test;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestJunit1 {

    String message = "Robert";
    MessageUtil messageUtil=new MessageUtil(message);

    @Test
    public void testSalutationMessage(){
        System.out.println("Inside testSalutationMessage()");
        message="Hi!"+"Robert";
        Assert.assertEquals(message,messageUtil.salutationMessage());
    }
}

测试执行类:

```
    import org.junit.runner.JUnitCore;
    import org.junit.runner.Result;
    import org.junit.runner.notification.Failure;

    /**
    * Created by Administrator on 2017/11/1 0001.
    */
    public class TestRunner2 {
        public static void main(String[] args) {
            Result result= JUnitCore.runClasses(JunitTestSuite.class);
            for (Failure failure:result.getFailures()){
                System.out.println(failure.toString());
            }
            System.out.println(result.wasSuccessful());
        }
    }
```
  • 运行结果
Inside testPrintMessage()
Robert
Inside testSalutationMessage()
Hi!Robert
true

6. 忽略测试

  • 一个含有@Ignore 注释的测试方法不会被执行
  • 如果一个测试类有@Ignore注释,则它的测试方法将不会被执行

7. 时间测试

  • timeout参数与@Test注释一起使用,当一个测试用例比指定毫秒数花费更多时间,那么Junit将会自动将它标记为失败。
  • 代码示例:
/**
* Created by Administrator on 2017/11/1 0001.
*/
public class MessageUtil {

    private String message;

    public MessageUtil(String message){
        this.message=message;
    }

    public void printMessage(){
        System.out.println(message);
        while (true);
    }

    public String salutationMessage(){
        message="Hi!"+message;
        System.out.println(message);
        return message;
    }
}
import org.junit.Assert;
import org.junit.Test;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestJunit4 {

    String message = "Robert";
    MessageUtil messageUtil=new MessageUtil(message);


    @Test(timeout = 1000)
    public void testPrintMessage(){
        System.out.println("Inside testPrintMessage");
        messageUtil.printMessage();
    }

    @Test
    public void testSalutationMessage(){
        System.out.println("Inside testSalutationMessage");
        message="Hi!"+"Robert";
        Assert.assertEquals(message,messageUtil.salutationMessage());
    }
}
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestRunner2 {
    public static void main(String[] args) {
        Result result= JUnitCore.runClasses(TestJunit4.class);
        for (Failure failure:result.getFailures()){
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}
  • 运行结果
Inside testSalutationMessage
Hi!Robert
Inside testPrintMessage
Robert
testPrintMessage(TestJunit4): test timed out after 1000 milliseconds
false

8. 异常测试

  • expected参数与@Test注释一起使用,可以测试代码是否抛出你想要得到的异常。
  • 代码示例
/**
* Created by Administrator on 2017/11/1 0001.
*/
public class MessageUtil {

    private String message;

    public MessageUtil(String message){
        this.message=message;
    }

    public void printMessage(){
        System.out.println(message);
//        return message;
//        while (true);
        int a=0;
        int b=1/a;
    }

    public String salutationMessage(){
        message="Hi!"+message;
        System.out.println(message);
        return message;
    }
}
import org.junit.Assert;
import org.junit.Test;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestJunit5 {

    String message = "Robert";
    MessageUtil messageUtil=new MessageUtil(message);

    @Test(expected = ArithmeticException.class)
    public void testPrintMessage(){
        System.out.println("Inside testPrintMessage");
        messageUtil.printMessage();
    }

    @Test
    public void testSalutationMessage(){
        System.out.println("Inside testSalutationMessage");
        message="Hi!"+"Robert";
        Assert.assertEquals(message,messageUtil.salutationMessage());
    }
}
  • 运行结果
Inside testSalutationMessage
Hi!Robert
Inside testPrintMessage
Robert
true

9. 参数化测试

  • 参数化测试允许开发人员使用不同的值反复运行同一个测试用例。
  • @RunWith(Parameterized.class)来注释test类
  • 创建一个由@Parameterized.Parameters注释的公共静态方法,返回一个对象的集合(数组)来作为测试数据集合。
  • 创建一个公共构造函数,用来接收和存储测试数据。
  • 用例会反复运行,为每一组测试数据创建一个实例变量
  • 用实例变量作为测试数据的来源来创建你的测试用例
  • 代码示例
    判断是不是素数:
public class PrimeNumberChecker {

    public Boolean validate(final Integer primeNumber){
        for (int i = 2; i < (primeNumber / 2); i++) {
            if (primeNumber%i==0){
                return false;
            }
        }
        return true;
    }
}
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.Collection;

/**
* Created by Administrator on 2017/11/1 0001.
*/

@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {

    private Integer inputNumber;
    private Boolean expectedResult;
    private PrimeNumberChecker primeNumberChecker;

    @Before
    public void initialize(){
        System.out.println("initialize");
        primeNumberChecker=new PrimeNumberChecker();
    }

    public PrimeNumberCheckerTest(Integer inputNumber,Boolean expectedResult){
        System.out.println("construct");
        this.inputNumber=inputNumber;
        this.expectedResult=expectedResult;
    }

    @Parameterized.Parameters
    public static Collection primeNumbers(){
        return Arrays.asList(new Object[][]{
                {2,true},
                {6,true},
                {19,true},
                {22,true},
                {23,true}
        });
    }

    @Test
    public void testPrimeNumberChecker(){
        System.out.println("test");
        System.out.println("Parameterized Number is : " + inputNumber);
        Assert.assertEquals(expectedResult,
                primeNumberChecker.validate(inputNumber));
    }
}
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

/**
* Created by Administrator on 2017/11/1 0001.
*/
public class TestRunner2 {
    public static void main(String[] args) {
        Result result= JUnitCore.runClasses(PrimeNumberCheckerTest.class);
        for (Failure failure:result.getFailures()){
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}
  • 运行结果
construct
initialize
test
Parameterized Number is : 2
construct
initialize
test
Parameterized Number is : 6
construct
initialize
test
Parameterized Number is : 19
construct
initialize
test
Parameterized Number is : 22
construct
initialize
test
Parameterized Number is : 23
testPrimeNumberChecker[1](PrimeNumberCheckerTest): expected:<true> but was:<false>
testPrimeNumberChecker[3](PrimeNumberCheckerTest): expected:<true> but was:<false>
false