---
# System prepended metadata

title: Testing
tags: [eGroupAI 開發規範]

---

# Testing
###### tags: `eGroupAI 開發規範`

> 測試用意不是來證明你是對的，而是你沒有錯。

---

> 參考資料

- [開源框架：JUnit Gossip - OpenHome.cc](http://openhome.cc/Gossip/JUnit/index.html)
- [JUnit介紹,JUnit是什麼？](http://tw.gitbook.net/junit/what-is-junit.html#)

## JUnit單元測試
> 簡介

- JUnit是個單元測試（Unit test）框架
- 單元測試指的是測試一個工作單元（a unit of work）的行為e.g. 建築橋墩：一個螺絲釘、一根鋼筋、一條鋼索、一公斤的水泥，都可謂是一個工作單元。
- 單元之間獨立

> Basic Annotations 常用註解

- @Test：表示要執行此區段裡的測試碼
    - @Test(expected = Exception.class): 預期會拋出Exception.class的異常(e.g. 算術異常ArithmeticException)
    - @Test(timeout = 100)：測試方法預期執行不超過100**毫秒**
- @Before：每次測試前執行
- @After： 每次測試後執行
- @BeforeClass：在開始第一個測試前執行，只執行一次，必須為static void
- @AfterClass ：全部測試完畢後執行，只執行一次，必須為static void
- @Ignore("忽略原因可以寫在這")：忽略測試，測試方法不會被執行
- @Runwith：運行器(Runner)執行之類別，可進行更改
- @Parameters：測試用參數宣告

> 上述範例

```java
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class BeforeAndAfterTest {

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

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

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

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

    @Test
    public void test01() {
        System.out.println("Test01");
    }

    @Test
    public void test02() {
        System.out.println("Test02");
    }

}

```

執行結果：

```java
BeforeClass
Before
Test01
After
Before
Test02
After
AfterClass

```

**注意：1.所有測試方法必須使用public2.@BeforeClass與@AfterClass必須使用static void**
## 基本驗證
> JUnit 提供的 Assert 驗證方法

### **assertEquals**

- 值可能是類型有 int, short, long, byte, char or java.lang.Object
- 查看物件中存的值是否是期待的值，預期值(expected)=實際值(actual)

```
void assertEquals([String message], expected value, actual value)

//範例
assertEquals(expected, new Caculate().add(input1, input2));

```

### **assertTrue、assertFalse**

- boolan值
- 查看變數是是否為false或true

```
void assertTrue([String message], boolean condition)
void assertFalse([String message],boolean condition)    

//範例
assertTrue(expected, new Member().isLogin(input));
assertFalse(expected, new Member().isLogin(input));

```

### **assertNull、assertNotNull**

- 用來查看物件是否為空和不為空

```
void assertNull([String message], java.lang.Object object)
void assertNotNull([String message], java.lang.Object object)

//範例
@Test
public void sample_assert_null(){
    //arrange
    Object expectNull = null;
    Object expectNotNull = new Object();

    //assert
    Assert.assertNull(expectNull);
    Assert.assertNotNull(expectNotNull);
}

```

### **assertSame、assertNotSame**

- 用來比較兩個物件的引用是否相等和不相等

```
void assertSame([String message], java.lang.Object expected, java.lang.Object actual)
void assertNotSame([String message], java.lang.Object unexpected, java.lang.Object actual)

/*範例
*same 跟 equals 的差別其實就是平常使用 a == b 跟 a.equals(b) 是一樣的，看下面例子就可以了解
*/
@Test
public void sample_assert_same(){
    //arrange
    Object expect = new Object();
    Object expectNotSame = new Object();
    Object actual = expect;

    //assert
    Assert.assertSame(expect, actual);
    Assert.assertNotSame(expectNotSame, actual);
}

```

### **assertArrayEquals**

- 用來比較預期數組和結果數組相等

```
void assertArrayEquals([String message], expectedArray, resultArray)

//範例
@Test
public void sample_assert_array(){
    int[] expect = new int[]{1, 2, 3};
    int[] actual = new int[]{1, 2, 3};

    Assert.assertArrayEquals(expect, actual);
}

```

### **驗證集合**

```
//範例
@Test
public void sample_assert_collection(){
    List<Integer> expect = Arrays.asList(1, 2, 3);
    List<Integer> actual = Arrays.asList(1, 2, 3);

    Assert.assertEquals(expect, actual);
}
```
## 撰寫第一個測試
> 起手式

先把JUnit框架引入

- 專案右鍵->Build Path->Libraries->AddLibrary


- 選擇JUnit4


- 在src/test/java 路徑下新增測試程式


- 右鍵新增Junit 測試檔案


- 在測試檔案上，右鍵->Run As->Junit Test，即可進行Junit 測試。

> 測試檔案命名方式

模組名稱或要被測試類別的名稱+Test

```java
//e.g. 如果模組為:Caculate，模組測試則是CaculateTest
//模組
public class Caculate {
    public int add(int a,int b){
        return a+b;
    }

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

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

    public int divide(int a,int b){
        return a/b;
    }
}

//模組測試
public class CaculateTest {

    @Test
    public void testAdd() {
        assertEquals(3, new Caculate().add(2, 1));
    }

    @Test
    public void testSubtract() {
        assertEquals(8, new Caculate().subtract(9, 1));
    }

    @Test
    public void testMultiply() {
        assertEquals(6, new Caculate().multiply(3, 2));
    }

    @Test
    public void testDivide() {
        assertEquals(3, new Caculate().divide(9, 3));
    }

    @Ignore("可以在這說明忽略原因")
    @Test(timeout=2000)
    public void testWhile(){
        while (true) {        
            System.out.println("run forever");

        }
    }

    @Test(timeout=2000)
    public void readFile(){
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

}

```

由上述模組測試範例，可以看到其中一個測試方法，以testAdd()為例

```java
//assertEquals(預期值, 測試值(可以method回傳));
@Test
public void testAdd() {
    assertEquals(3, new Caculate().add(2, 1));
}

```

當預期值=測試值，代表其測試通過，反之，不通過。

> 參數化測試

- 使用多組數值進行測試

    ```java
    /*
    *1.更改默認的測試運行器Runner，@RunWith(Parameterized.class)
    *2.宣告變量來存放預設值跟結果值 
    *3.宣告一個返回值為Collection的公共靜態方法，並使用@Parameters進行修飾
    */
    @RunWith(Parameterized.class)
    public class ParameterTest {
      int expected=0;
      int input1=0;
      int input2=0;

      @Parameters
      public static Collection<Object[]> t(){
          return Arrays.asList(new Object[][]{
              {3,2,1},
              {5,2,3},
              {6,2,4}
          });
      }

      public ParameterTest(int expected,int input1,int input2){
          this.expected=expected;
          this.input1=input1;
          this.input2=input2;
      }

      @Test
      public void testAdd(){
          assertEquals(expected, new Caculate().add(input1, input2));
      }

    ```

> 測試套件 Suite

- 批量運行，把所有測試方法集何在一個測試套件中，只須執行該套件一次即可。

```java
/*
*1.同時去執行SuiteClasses中的所有測試類
*2.寫一個作為測試套件的入口類，這個類別裡面不包含其他方法
*3.更改默認的測試運行器Runner，@RunWith(Suite.class)
*4.將要測試之類別，傳到Suite.SuiteClasses({})
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({ 
    TaskTest1.class,
    TaskTest2.class,
    ParameterTest.class,
    Amount2RMBTest.class,
    CaseIDGeneratorTest.class
})
public class SuiteTest {

}

```

- 也能直接產生Suite測試套件 右鍵->新增 JUnit Test Suite->選擇勾選測試類

    ![https://laitaiyuan1.gitbooks.io/egroup/content/ssss.PNG](https://laitaiyuan1.gitbooks.io/egroup/content/ssss.PNG)

> 測試慣例

- 軟體運作良好的事物都是經過測試檢驗的。
- 測試一點點，程式碼寫一點點，測試一點點，程式碼寫一點點......
- 確認所有測試都能100％通過。
- 要測試的程式碼是最可能錯誤的區域。
- 撰寫程式碼之前先寫單元測試
- 程式功能模組化

## 壓力測試
### **Jmeter**

下載

[http://jmeter.apache.org/download_jmeter.cgi](http://jmeter.apache.org/download_jmeter.cgi)

教學

[http://cloudchen.logdown.com/posts/247932/apache-jmeter-tool-for-load-test-and-measure-performance](http://cloudchen.logdown.com/posts/247932/apache-jmeter-tool-for-load-test-and-measure-performance)