1. 程式人生 > >單元測試Junit

單元測試Junit

direct 期望 常用 parameter zed 程序員 rim ctu ror

###<center> 單元測試Junit </center>###
- - -
1.**單元測試**:
> ==單元測試==是軟件之中對於最小的功能模塊的的測試,其可以對最基本的軟件構成單元來測試。
> 需要註意的是:
> >**測試用例是用來達到測試想要的預期結果,而不能測試出程序的邏輯錯誤**。


2.**JUnit**:
>1.**Junit是基於斷言機制的**。是用於編寫可復用測試集的簡單框架,是xUnit的一個子集。xUnit是一套基於測試驅動開發的測試框架,有PythonUnit、CppUnit、JUnit等。
>2.Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什麽樣(What)的功能。
>3.多數Java的開發環境都已經集成了JUnit作為單元測試的工具,比如Eclipse,IDEA,如在IDEA之中,使用JunitGenerator可以方便地進行單元測試。

3.**使用測試工具的優點**:
- 測試框架可以幫助我們對編寫的程序進行有目的地測試,幫助我們最大限度地避免代碼中的bug,以保證系統的正確性和穩定性。
- 使用main方法,然後sysout輸出控制臺觀察結果,這樣非常枯燥繁瑣,不規範。並且**測試方法不能一起運行,測試結果要程序員本人才可以判斷程序邏輯是否正確**。
- JUnit的斷言機制,可以直接將我們的預期結果和程序運行的結果進行一個比對,確保對結果的可預知性。

4.**JUnit最佳實踐**:
- [email protected]
- ②測試方法必須使用public void 進行修飾,不能帶任何的參數
- ③源代碼與測試代碼不在同一目錄,即**測試代碼和項目業務代碼分離**
- ④測試類所在的包名應該和被測試類所在的包名保持一致
- ⑤測試單元中的每個方法必須可以獨立測試,測試方法間不能有任何的依賴
- ⑥測試類使用Test作為類名的後綴(不是必須)
- ⑦測試方法使用test作為方法名的前綴(不是必須)

5.**Junit常用註解**:
- @Test:將一個普通的方法修飾成為一個測試方法
- @Test(expected=XX.class)
- @Test(timeout=毫秒)
- @BeforeClass:它會在所有的方法運行前被執行,static修飾
- @AfterClass:它會在所有的方法運行結束後被執行,static修飾
- @Before:會在每一個測試方法被運行前執行一次
- @After:會在每一個測試方法運行後被執行一次
- @Ignore:所修飾的測試方法會被測試運行器忽略
- @Parameters:參數化測試時,對於測試參數進行配置
- @RunWith:可以更改測試運行器,==org.junit.runners.JUnit4==是junit4之中默認的運行器,其繼承於==org.junit.runners.BlockJUnit4ClassRunner==,常見的運行器有 [email protected]

/* */(Parameterized.class)* 參數化運行器,[email protected],[email protected](Suite.class)
@SuiteClasses({ATest.class,BTest.class,CTest.class})*
[email protected](JUnit4.class)*,junit4的默認運行器,[email protected](JUnit38ClassRunner.class)*,junit3.8的運行器,還有例如*RunWith(SpringJUnit4ClassRunner.class)*運行器,集成了spring的一些功能。

6.**JUnit測試的類型**:
- *普通測試*:[email protected]==註解標註。
- *參數化測試*:[email protected] (Parameterized.class)==標註,使用==@(Parameter)==來將需要測試的參數,打包到一個集合之中,在後續的測試方法中運行。
- *套件測試*:當我們需要測試的類很多的時候,如果每個類都獨立運行則工作量很大,因此我們可以采用測試套件的方式來測試各個測試類和用例,我們可以使用如下的:
[email protected](Suite.class)
@Suite.SuiteClasses({TaskTest1.class,TaskTest2.class,TaskTest3.class})
public class SuiteTest {
//此類之中不需要任何的方法
}[^1]
==

- *超時測試*:[email protected](timeout=毫秒)設置一定的值,常用於如IO操作,網絡連接等測試。
- *忽略測試*:[email protected],將對應的測試類或者測試方法忽略。

7.**JUint斷言方法**:

Junit 4 斷言方法允許檢查測試方法的期望結果值和真實返回值。Junit的org.junit.Assert類提供了各種斷言方法來寫junit測試。這些方法被用來檢查方法的真實結果值和期望值。下列一些有用的斷言方法列表:

**Junit 4 Assert Methods**

| Method | Description |
|---------------------------------------------|---------------------|
|assertNull(java.lang.Object object) | 檢查對象是否為空 |
|assertNotNull(java.lang.Object object) | 檢查對象是否不為空 |
|assertEquals(long expected, long actual) | 檢查long類型的值是否相等 |
|assertEquals(double expected, double actual, double delta) | 檢查指定精度的double值是否相等 |
|assertFalse(boolean condition) | 檢查條件是否為假 |
|assertTrue(boolean condition) | 檢查條件是否為真 |
|assertSame(java.lang.Object expected, java.lang.Object actual)| 檢查兩個對象引用是否引用同一對象(即對象是否相等)|
|assertNotSame(java.lang.Object unexpected, java.lang.Object actual) | 檢查條件是否為真|
|assertTrue(boolean condition) | 檢查兩個對象引用是否不引用統一對象(即對象不等) |
至於Assert類,其文檔之中介紹如下:
>A set of assertion methods useful for writing tests. Only failed assertions are recorded. These methods can be used directly: Assert.assertEquals(...), however, they read better if they are referenced through static import:
> >import static org.junit.Assert.*;
...
assertEquals(...);

>其最重要的是可以直接調用==Assert.assertEquals(...)==。


8**使用案例**:

==*Area.java*==

```
package Task;
/**
* Created by renjiaxin on 2017/7/25.
* 長方形面積
*/
public class Area {
private double width;
private double length;

public Area(double w, double l) {
this.width = w;
this.length = l;
}

public void setWidth(double width) {
this.width = width;
}

public void setLength(double length) {
this.length = length;
}

public double getWidth() {
return width;
}

public double getLength() {
return length;
}

public double getArea(double width, double length) {
return width * length;
}
}
```

==*Calcuate.java*==
```
package Task;
/**
* Created by renjiaxin on 2017/7/25.
* 四則運算
*/
public class Calcuate {
private int a;
private int b;

public Calcuate(int a, int b) {
this.a = a;
this.b = b;
}

public void setA(int a) {
this.a = a;
}

public void setB(int b) {
this.b = b;
}

public int getA() {
return a;
}

public int getB() {
return b;
}

public int addition(int a, int b) {
return a + b;
}

public int subtraction(int a, int b) {
return a - b;
}

public int multiplication(int a, int b) {
return a * b;
}

public int division(int a, int b) throws Exception {
if (b != 0) {
return a / b;
} else {
throw new Exception("Error,b=0!");
}
}
}

```

==*Perimeter.java*==
```
package Task;
/**
* Created by renjiaxin on 2017/7/25.
* 周長
*/
public class Perimeter {
private double diameter;

public Perimeter(double diameter) {
this.diameter = diameter;
}

public double getDiameter() {
return diameter;
}

public void setDiameter(double diameter) {
this.diameter = diameter;
}

public double getPerimeter(double diameter) {
return 2 * Math.PI * diameter;
}

}
```

==*Tax.java*==
```
package Task;
/**
* Created by renjiaxin on 2017/7/25.
* 稅收
*/
public class Tax {
private double taxrate;
private double count;

public double getTaxrate() {
return taxrate;
}

public void setTaxrate(double taxrate) {
this.taxrate = taxrate;
}

public double getCount() {
return count;
}

public void setCount(double count) {
this.count = count;
}

public double getAllTax() {
return this.count * this.taxrate;
}
}
```

==*AreaTest.java*==
```
package ASuiteTestForTask;

import Task.Area;
import org.junit.*;
/**
* Created by renjiaxin on 2017/7/25.
*/
public class AreaTest {

@Before
public void before() throws Exception {
System.out.println("開始測試.");
}

@After
public void after() throws Exception {
System.out.println("測試完成.");
}

@Test
public void testSetWidth() throws Exception {
//double時,需要指出誤差,此處的1即為誤差,在此範圍內,可以通過
Area area = new Area(9.6, 12.8);
area.setWidth(16.2);
Assert.assertEquals(16, area.getWidth(),1);
}

@Test
public void testGetWidth() throws Exception {
Area area = new Area(9.6, 12.8);
Assert.assertEquals(9.6, area.getWidth(),0.01);
}

@Test(timeout = 200)
public void testSetLength() throws Exception {
//timeout 超時測試
Thread.sleep(100);
Area area = new Area(9.6, 12.8);
area.setLength(11.3);
Assert.assertEquals(11.3, area.getLength(),0.01);
}

@Ignore
public void testGetLength() throws Exception {
//忽略測試,將護忽略此方法,如果添加到類上面,將整個忽略此類的方法,
//可以用來跳過某些失敗的測試
Area area = new Area(9.6, 12.8);
Assert.assertEquals(12.8, area.getLength(),0.01);
}

@Test
public void testGetArea() throws Exception {
Area area = new Area(9.6, 12.8);
Assert.assertEquals(9.6 * 12.8, area.getArea(area.getWidth(), area.getLength()),0.01);
}
}
```

==*CalcuateTest.java*==
```
package ASuiteTestForTask;

import Task.Calcuate;
import org.junit.*;

/**
* Created by renjiaxin on 2017/7/25.
*/
public class CalcuateTest {

@BeforeClass
public static void before() throws Exception {
System.out.println("開始測試.");
}

@AfterClass
public static void after() throws Exception {
System.out.println("測試完成.");
}

@Test
public void testAddition() throws Exception {
Calcuate calcuate=new Calcuate(12,10);
Assert.assertEquals("加法有問題!",22,calcuate.addition(calcuate.getA(),calcuate.getB()));
}

@Test
public void testSubtraction() throws Exception {
Calcuate calcuate=new Calcuate(12,10);
Assert.assertEquals("減法有問題!",2,calcuate.subtraction(calcuate.getA(),calcuate.getB()));
}

@Test
public void testMultiplication() throws Exception {
Calcuate calcuate=new Calcuate(12,10);
Assert.assertEquals("乘法有問題!",120,calcuate.multiplication(calcuate.getA(), calcuate.getB()));
}

@Test(expected = Exception.class)
public void testDivision() throws Exception {
Calcuate calcuate1=new Calcuate(12,10);
Assert.assertEquals("除法有問題!",1,calcuate1.division(calcuate1.getA(), calcuate1.getB()));
//通過expected來指出可能的錯誤,如果合適,就可通過測試。
Calcuate calcuate2=new Calcuate(12,0);
Assert.assertEquals("除法有問題!",120,calcuate2.division(calcuate2.getA(), calcuate2.getB()));
}
}
```

==*PerimeterTest.java*==
```
package ASuiteTestForTask;

import Task.Perimeter;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;

/**
* Created by renjiaxin on 2017/7/25.
*/
public class PerimeterTest {

Perimeter perimeter;

@Test
public void testSetDiameter() throws Exception {
perimeter=new Perimeter(12);
perimeter.setDiameter(16);
Assert.assertEquals(16,perimeter.getDiameter(),0.001);
}

@Test
public void testGetDiameter() throws Exception {
perimeter=new Perimeter(12);
Assert.assertEquals(12,perimeter.getDiameter(),0.001);
}

@Test
public void testGetPerimeter() throws Exception {
perimeter=new Perimeter(12);
Assert.assertEquals(24*Math.PI,perimeter.getPerimeter(perimeter.getDiameter()),0.001);
}
}

```

==*TaxTest.java*==
```
package ASuiteTestForTask;

import Task.Tax;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;


public class TaxTest {
Tax tax=new Tax();

@Test
public void testgetTaxrate() {
tax.setTaxrate(0.2);
Assert.assertEquals(0.2,tax.getTaxrate(),0.01);
}

@Test
public void testSetTaxrate() {
tax.setTaxrate(0.2);
Assert.assertEquals(0.2,tax.getTaxrate(),0.01);
}

@Test
public void testSetCount() {
tax.setCount(20000);
Assert.assertEquals(20000,tax.getCount(),1);
}

@Test
public void testGetCount() {
tax.setCount(20000);
Assert.assertEquals(19999,tax.getCount(),1);
}

@Test
public void testGetAllTax() {
tax.setCount(20000);
tax.setTaxrate(0.17);
Assert.assertEquals(3500,tax.getAllTax(),199);
}
}
```

==*TaskTestSuite.java*== 測試套件
```
package ASuiteTestForTask;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

/**
* Created by renjiaxin on 2017/7/25.
* 測試套件,同時可以運行多個測試類
*/
[email protected],或者類繼承了一個被該註解修飾的類,
* JUnit將會使用這個註解所指明的運行器(runner)來運行測試,
* 而不是JUnit默認的運行器。
**/
@RunWith(Suite.class)
@Suite.SuiteClasses({AreaTest.class, CalcuateTest.class, PerimeterTest.class, TaxTest.class})
public class TaskTestSuite {
/*
* 測試套件就是組織測試類一起運行的
* 寫一個作為測試套件的入口類,這個類裏不包含其他的方法
* 更改測試運行器Suite.class
* 將要測試的類作為數組傳入到Suite.SuiteClasses({})
*/
}
```

==*AreaParameterizedTest.java*== :參數化測試
```
package ASuiteTestForTask;

import Task.Area;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;

/**
* Created by renjiaxin on 2017/7/25.
* 參數化測試 Parameterized Test
*/
// Step 1
@RunWith(Parameterized.class)
public class AreaParameterizedTest {
/*
* 1.測試類必須由Parameterized測試運行器修飾
* 2.準備測試所需的數據
* 3.構造此類的參數化構造器
* 4.準備需要測試的數據,將其放置在Collection之中
* 5.創建測試方法
**/
// Step 2: variables to be used in test method of Step 5
private double width;
private double length;
private double expected;

// Step 3: parameterized constructor
public AreaParameterizedTest(double width, double length, double expected) {
this.width = width;
this.length = length;
this.expected = expected;
}

// Step 4: data set of variable values
@Parameterized.Parameters
public static Collection preparedData() {
Object[][] object = {{1, 2, 2}, {4, 6, 24}, {3, 9, 27}, {4, 9, 36}, {2.5, 4, 10}, {12, 1.8, 21.6}};
return Arrays.asList(object);
}

// Step 5: use variables in test code
@Test
public void testGetSArea() {
Area area = new Area(width, length);
double result = area.getArea(width, length);
Assert.assertEquals(expected, result, 0.1);
}
}
```


Ref:
[junit常用註解詳細說明](http://www.cnblogs.com/tobey/p/4837495.html),
[Java單元測試工具:JUnit4(一)——概述及簡單例子](http://blog.csdn.net/Zen99T/article/details/50561136),
[Java單元測試工具:JUnit4(二)——JUnit使用詳解](http://blog.csdn.net/zen99t/article/details/50603847),
[ Java單元測試工具:JUnit4(三)——JUnit詳解之運行流程及常用註解](http://blog.csdn.net/Zen99T/article/details/50569297),
[Java單元測試工具:JUnit4(四)——JUnit測試套件使用及參數化設置](http://blog.csdn.net/Zen99T/article/details/50572373)
[Junit 4 Tutorials(Junit 4 教程) 一、Junit簡介及Junit Eclipse 教程](http://blog.csdn.net/luanlouis/article/details/37562165),
[Junit 4 Tutorials(Junit 4 教程) 二、Junit4 註解](http://blog.csdn.net/luanlouis/article/details/37562289),
[Junit 4 Tutorials(Junit 4 教程) 三、Junit4 斷言方法](http://blog.csdn.net/luanlouis/article/details/37562777),
[Junit 4 Tutorials(Junit 4 教程) 四、Junit4 參數化測試](http://blog.csdn.net/luanlouis/article/details/37563265),
[Junit 4 Tutorials(Junit 4 教程) 五、測試套件](http://blog.csdn.net/luanlouis/article/details/37564355),
[Junit 4 Tutorials(Junit 4 教程) 六、忽略測試](http://blog.csdn.net/luanlouis/article/details/37565017),
[Junit 4 Tutorials(Junit 4 教程) Junit4 七、超時測試](http://blog.csdn.net/luanlouis/article/details/37565709),

ReadMe[^2]

[^1]:測試套件就是組織測試類一起運行的,寫一個作為測試套件的入口類,這個類裏不包含其他的方法,更改測試運行器Suite.class ,將要測試的類作為數組傳入到Suite.SuiteClasses({}),此處的類命可以隨意命名,TaskTestX.class是需要測試的類。
[^2]:本文檔和文檔之中的代碼,都基於JUnit4.

單元測試Junit