# 單元測試的 3 個撰寫方法
每個被測單元 (Unit) 的測試案例 (Test Case) 都有 3A 原則的 **Arrange, Act, Assert** 這 3 個階段。
### 方法 1 驗證目標物件的回傳值

```typescript
// return-value.ts
export const sum = (a, b) => a + b;
// return-value.spec.ts
import { sum } from './sum'
describe('sum()', () => { // Unit
it('return 2, when a = 1 b = 1', () => { // Test case
// Arrange
const a = 1, b = 1;
// Act
const result = sum(a, b);
// Assert 驗證 回傳值為預期結果
expect(result).toEqual(2);
});
});
```
### 方法 2 驗證目標物件的狀態改變

```typescript
// state-change.ts
export const service = {
c: 0,
changeC = (v) => {
this.c = v;
},
};
// state-change.spec.ts
import { service } from './state-change'
describe('changeC()', () => { // Unit
it('c = 3, when v = 3', () => { // Test case
// Arrange
const result = 3;
// Act
service.changeC(3);
// Assert 驗證 service.c 狀態被改變
expect(service.c).toEqual(result);
});
});
```
### 方法 3 驗證目標物件與外部相依介面的互動方式

```typescript
// interaction.ts
export const service = {
getFromApi: () => 'data',
data: null,
setToStore: d => { this.data = d; },
getData: () => {
const d = this.getFromApi();
this.setToStore(d);
return d;
}
};
// interaction.spec.ts
import { service } from './interaction'
describe('getData()', () => { // Unit
it('call sev.getFromApi(), sev.setToStore()', () => { // Test case
// Arrange 準備 被測單元相依的物件/行為
const getFromApi = spyOn(service, 'getFromApi'); // 準備假的 getFromApi
const setToStore = spyOn(service, 'setToStore'); // 準備假的 setToStore
// Act 動作
service.getData(); // 使用 getData
// Assert 驗證 有使用相依的物件/行為
expect(getFromApi).toHaveBeenCalled(); // 假的 getFromApi 應該要被 getData 使用
expect(setToStore).toHaveBeenCalled(); // 假的 setToStore 應該要被 getData 使用
});
});
```
### 對應 驗收測試 的格式
單元測試 的撰寫格式,也可以對應 驗收測試
| Acceptance Test | Unit Test |
| --------------- | --------- |
| Feature | Unit |
| Scenario | Test Case |
| Given | Arrange |
| When | Act |
| Then | Assert |
### 總結
* 3A 原則: **Arrange, Act, Assert**
* 測試方法:
* 驗證目標物件的 **回傳值**
* 目標物件的 **狀態改變**
* 目標物件與 **相依物件/行為的互動** 方式