# 自動化測試簡介及導入
- [常見自動測試介紹](#常見自動測試介紹)
- [E2E testing(Cypress)導入](#E2E-testing-\(Cypress\)導入)
- [參考資料](#參考資料)
## 常見自動測試介紹
### 單元測試 (Unit testing)
> 以「測試引擎」及「斷言庫」所組成的測試類型。以程式碼的最小單位 (function) 進行測試,保護程式邏輯不會在 ***系統維護、重構*** 的過程中遭到破壞,並確保程式碼品質
**Pros**
1. 測試以 function 為主,因此可以得到較高的撰寫效益,是最容易撰寫的測試類型。
2. 測試即文件,若有豐富的測例,可減少閱讀或重構程式碼時茫然的機會
**Cons**
Time is money,初學時會花較多時間,導致放棄撰寫單元測試。
#### 常見工具
| 類型 | 名稱 |
| -------- | -------- |
| 測試引擎 | Mocha, Jest |
| 斷言庫 | chai, power-assert |
**Tips**
可以在 Vscode 裝 Jest 的擴充功能,可以直接在 editor 看測試結果

-------------
### 端對端測試 (End-to-end testing)
> Aka E2E tesing,直接以完整 UI、實際系統進行測試,開啟瀏覽器並模擬使用者的真實操作,然後診斷其結果是否符合預期。很可惜的是,大部分的端對端測試都是人工進行的
**Pros**
人非聖賢、孰能無過,人工測試總有疏漏之處,既便有完整的測試案例與腳本,測試人員或是自己也不見得會每次照著 SOP 進行操作
**Cons**
若以真實後端打 API,可能有非預期的錯誤(childrens, foots, totalSize 為 0 ...等等),故可能需多維護一個純 mock 版的測試(直接寫死 response),只測前端功能(用以甩鍋)
### 常見工具
*I don't know which is the best; however, we are all the best*
| 名稱 | 學習成本 | 速度 | 瀏覽器支援度 | 優點 | 雷點 |
| -------- | -------- | -------- | -------- | -------- | -------- |
| Cypress | 低 | 快 | Chrome 友善 | 好寫好用 | 不支援 ES7, Native Browser Events
| Testcafe | 中 | 快 | 貌似皆可 | 還沒用過 | 寫法較特殊 |
| Puppeteer | 高 | 快 | Chrome only | 還可以用來爬蟲 | ? |
| Selenium Webdriver | 高 | 跟 IE 一樣 | 高 | 老牌,執行後可以去泡咖啡 | ? |
----
## E2E testing(Cypress)導入
### 1. 安裝
* vue-cli 建專案時就選他
* npm install cypress -D
沒裝過的話會幫自動產出相關 json config, example files
```
./
│ cypress
│ │
│ └───fixtures(放 data, user 之類的 json)
│ │ │ ...
│ │
│ └───integration(測試文件區)
│ │ │
│ │ └───examples
│ │ │ │ example1.spec.js
│ │ │ │ example2.spec.js
│ │ │ │ ...
│ │ │
│ │ └───tests
│ │ │ login.spec.js
│ │ │ ...
│ │ │
│ └─--plugins
│ │ │ ...
│ │
│ └─--support(自訂指令)
│ │ ...
│
└───src
│ │ App.vue
│ │ ...
│
│ cypress.json
└───package.json
```
### 2. 調整 eslint (若無 eslint 可忽略)
> 在 env 區塊中加入 cypress,讓 cypress 的 keywords 不會被當成未宣告變數
```javascript=
// .eslintrc.js
env: {
...
'cypress/globals': true,
},
```
### 3. 新增測例
> 以 describe 包裝測試類型,it 區隔測例
```javascript=
// cypress/integration/tests/login.spec.js
describe('登入畫面', () => {
it('Login button exists (登入 button 存在)', () => {
cy.visit('/')
cy.contains('button', '登入')
})
})
```
### 4. 在 html 元素中加入 cypress 專用識別屬性 data-cy
就只是個一般的屬性而已,用來作為 selector

但這樣對 element-ui 的 組件會無效,所以需自訂 directive
```javascript=
import util from '@js/util'
const addCy = {
bind: async (el, bindings) => {
await util.delay(0)
const { arg, modifiers, expression } = bindings
const [label, relation, selector] = [arg, modifiers, expression]
if (selector && relation.parent) {
const selectEl = el.closest(selector.substring(1, selector.length - 1))
selectEl.setAttribute('data-cy', label)
} else if (selector && relation.child) {
const selectEl = el.querySelector(selector.substring(1, selector.length - 1))
selectEl.setAttribute('data-cy', label)
} else {
el.setAttribute('data-cy', label)
}
},
}
export default addCy
```

## 參考資料
- [保哥 - 一次搞懂單元測試、整合測試、端對端測試之間的差異](https://blog.miniasp.com/post/2019/02/18/Unit-testing-Integration-testing-e2e-testing)
- [Why Cypress? - Cypress Official Document](https://docs.cypress.io/guides/overview/why-cypress.html#In-a-nutshell)