# 三之 1、 JS 102 測試方法
###### tags: `JS102` `JavaScript續` `2020七月第五週` `進度筆記` `Lidemy心得` `7/29
---
## 怎麼測試寫的程式 , function 寫得對不對 ?
通常一開始蠻常用 `console.log` , 或是用邊界條件 (edge case) 去測試,但這樣會有很多的 `console.log` , 不是很容易跑。
因此可能在 `console.log` 內加上 `===` 做測試資料。
因此不好規模化,很難大量執行。
---
# 三之 1、 JS 102 Jest 使用前輩們的框架
###### tags: `JS102` `JavaScript續` `2020七月第五週` `進度筆記` `Lidemy心得` `7/29
---
Jest 可以幫助測試的 framework , 可以跟著使用:
[jestjs.io](https://jestjs.io/docs/en/getting-started.html)
[Jest | 讓 Jest 為你的 Code 做測試-基礎用法教學](https://medium.com/enjoy-life-enjoy-coding/%E8%AE%93-jest-%E7%82%BA%E4%BD%A0%E7%9A%84-code-%E5%81%9A%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6-%E5%9F%BA%E7%A4%8E%E7%94%A8%E6%B3%95%E6%95%99%E5%AD%B8-d898f11d9a23)
## jest 啟用方式
yarn : `yarn add --dev jest`
使用 yarn 的話因為預設安裝比較多 library , 所以測試結果比較有花俏的顏色、漂亮的結果。
npm : `npm install --save-dev jest`
npm 執行的話則要多安裝另外的 library , 不然顏色比較單一難以辨別。
如果用 yarn 指令, 會發現他載了蠻多 library :

---
## 測試與 code
通常測試歸測試,本身的 function 是本身的,是分開進行,所以會把 function exports 出去,如 `module.exports = 函式名` 。
所以我們先建立一個函式(其實已是模組):
```
function repeat(str, times) {
var result = ''
for(var i=0; i<times; i++) {
result += str
}
return result
}
module.exports = repeat
```
檔名是 index.js 。
---
再建立一個測試用的檔案:
`touch index.test.js`
檔名是 index.test.js 。
接著開啟,輸入:
```
var repeat = require('./index')
console.log(repeat('a', 5))
```
去引用 index.js , 成功後會看到:

---
## 套用 jest
把這段套到 index.test.js 檔案內,置換掉 `console.log(repeat('a', 5))` :
```
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
```
ES6 語法`() => {}` 其實等於函式 `function() {}` 。
---
而我們希望印出 5 倍的 a 祝福量,因此:
```
var repeat = require('./index')
test('a 應該重複 5 次應該要等於 aaaaa', function() {
expect(repeat('a', 5).toBe('aaaaa');
});
```
jest 提供一個 test 的 function , 像是(輸入中文也可) test('a 應該重複 5 次應該要等於 aaaaa', function() {
expect(repeat('a', 5).toBe('aaaaa');
});
expect (裡面接函式的結果),讓 a 重複 5 次,結果 (.toBe) 'aaaaa' , 可以這樣看,__這個 reapeat 的結果,我們 expect 它應該要是 toBe 多少__。
或是也可以這樣寫:
```
var repeat = require('./index')
function test1() {
expect(repeat('a', 5).toBe('aaaaa');
}
test('a 應該重複 5 次應該要等於 aaaaa', test1);
```
---
## 在 script 中設定指令
寫好後在 CLI 下用 node 指令跑會出錯,因此必須去更新 package.json (如果是跟 npm 裝在同一個 node_modules 資料夾下) 的 scripts 修改:

在 devDependencies 下將 `echo \"Error: no test specified\" && exit 1` 換成 `jest index.test.js` 。
並將 `"scripts": { "test": "jest --coverage" }`,為其加上 --coverage 參數,可看測試報告。
---
## 測試檔案該放哪 ?
test 檔案先放入 __test__ 資料夾內 , 而 js 檔案可放入 JS 資料夾。
jest 預設是 , 先找 __test__ 資料夾內的檔案來做測試。
如果有啟用資料夾管理,在 test 中引入的 js 檔案,則需注意檔案路徑。
之後就使用 jest 來跑 (不是 node 指令),因為 jest 不是 global 的安裝在電腦內,它是局部的裝在某個資料夾。
jest 全域指令:
`npm install jest -g // -g 代表全域;`
---
## 還沒好,安裝 babel 並設置檔案 config
使用指令安裝 babel `yarn add --dev babel-jest @babel/core @babel/preset-env` 。
接著可以用指令 `JEST init` , 修改配 jest 配置,如果 jest 不是全域安裝,則需使用 `yarn JEST --init` 。
參考文章:
[JEST 單元測試學習筆記 | Getting Started、Using Matchers](https://medium.com/unalai/jest-%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-getting-started-using-matchers-6f99ca314ca8)
[使用 Jest 進行 Unit Test](https://medium.com/@pvt5r486/%E4%BD%BF%E7%94%A8-jest-%E9%80%B2%E8%A1%8C-unit-test-ab53472990fc)
[Jest | 讓 Jest 為你的 Code 做測試-基礎用法教學](https://medium.com/enjoy-life-enjoy-coding/%E8%AE%93-jest-%E7%82%BA%E4%BD%A0%E7%9A%84-code-%E5%81%9A%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6-%E5%9F%BA%E7%A4%8E%E7%94%A8%E6%B3%95%E6%95%99%E5%AD%B8-d898f11d9a23)
---
## 照著跑出了點問題
但我照著, [jest.io](https://jestjs.io/docs/en/getting-started.html) 的 getting start 下去跑,一開始 sum.js 和 sum.test.js 的檔案放同層(同資料夾內),使用指令 `yarn test` 也可用 `npm run test` 或是 `yarn run test` (因為之前有在 scripts 下更改過 test 的指令) , 但顯示 fail 。
把 sum.test.js 更改 , require 的路徑,指定路徑也都沒效:
```
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
```

用指令 `npm run test` 或是 `yarn run test` 均失敗...
---
後來直到這篇文章 [Node.js中的require是如何工作的?](https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/662167/) 解決了我的問題;
原來 require 照著同層的位置跑,之後會再找上一層(其實課程內也有說到,不過不過我沒想到會有點後來去查才知道運作方式) , 好 , 所以我就更改了:
```
const sum = require('../sum'); // 就是這個 . 卡了我一陣 囧;
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
```

我把 sum.js 放在 _test_ 資料夾的上面一層資料夾(之前讀不到) ,然後重新指定他讀上一層,總算才成功。
---
## 也可以針對單一檔名跑:
可以將 package.json 複製到測試資料夾,然後在 package.json 檔案內找到 scripts 後,修改:
```
"scripts": {
"test": "jest index.test.js --coverage", // 可以指定 單一檔名,例如這次我指定了 index.test.js ;
"test1": "node index.js",
"test2": "node index2.js",
"serial": "node index.js && node index2.js"
```
---
而測試資料夾內跟 packags.json 檔案同一層的是, index.test.js 測試用檔案,內容為套用 jest 的形式:
```
var repeat = require('../index') // 前面加了 ../ 會指定上一層的 index.js 檔案;
test('a 應該重複 5 次應該要等於 aaaaa', function() {
expect(repeat('a', 5)).toBe('aaaaa');
});
```
而課程中測試的 index.js 模組檔案內的函式放在上一層資料夾:
```
function repeat(str, times) {
var result = ''
for(var i=0; i<times; i++) {
result += str
}
return result
}
module.exports = repeat
```
---
再用 `yarn run test` 指令可以看到:

---
全域指令:
`npx jest` 也可以指定檔案,例如: `npx jest index.test.js` , 比較新的 npm 版本會附上這個 npx , 這會從系統下去找:

中文敘述顯示亂碼的話要套用其他套件。
---
## 可以多來幾個 test ~
如果在想要用多一點方式來測試函數 (模組) 的功能,可以在測試檔案內這樣做,例如:
```
var repeat = require('../index') // 前面加了 ../ 會指定上一層的 index.js 檔案;
test('a 應該重複 5 次應該要等於 aaaaa', function() {
expect(repeat('a', 5)).toBe('aaaaa');
});
test('abc 應該重複 1 次應該要等於 aaaaa', function() {
expect(repeat('abc', 1)).toBe('abc');
});
test('"" 應該重複 10 次應該要等於 ""', function() {
expect(repeat('', 1)).toBe('');
});
```

一個 test 就是一行,多行 test 代表可以同時測試好幾個方法。
會比用 `console.log` 手動慢慢測試,更方便一點。
---
## 多行測試可以再集中一點的組合-單元測試
單元測試 (Unit test) 是可以將測試函數 (模組) 的功能,在測試檔案內這樣做集中組合,例如:
```
var repeat = require('../index') // 前面加了 ../ 會指定上一層的 index.js 檔案;
describe('測試 repeat', function(){
test('a 應該重複 5 次應該要等於 aaaaa', function() {
expect(repeat('a', 5)).toBe('aaaaa');
});
test('abc 應該重複 1 次應該要等於 aaaaa', function() {
expect(repeat('abc', 1)).toBe('abc');
});
test(''"" 應該重複 10 次應該要等於 ""', function() {
expect(repeat('', 1)).toBe('');
});
})
```
這樣寫比較有結構,像是把這些測試都歸在跑測試 repeat 的群組下。

---
# 三之 2、 JS 102 先測試再開發
###### tags: `JS102` `JavaScript續` `2020七月第五週` `進度筆記` `Lidemy心得` `7/29
測試驅動開發 (Test-Driven Development ) 。
接到一個需求 -> 寫好 function -> 測試,但 TDD 的精神是倒過來,亦先從測試開始。
範例:
1. 先從測試開始,且盡量寫得詳細點,盡量 try 邊角案例之類,但沒有寫任何 function 或是 code , 所以會看到 jest 測試是 fail 的情況。
2. 這樣就會知道要改哪,然後開始寫個大概的 function 或是 code , 寫完後開始抓錯,找 bug 。
3. 逐步修正,更新版本,檢查程式碼,每改一次 code 就執行測試直到對為止。
好處是希望去掉多餘的 code , 來提高質量,避免過度設計的浪費成本; 缺點是有可能會忽略實際使用需求,開發速度變慢、維護工作量增加等等。
Z>B 或是 B>Z 的正反觀點爭議蠻多~
---
參考文章:
[Test-Driven Development 解決了什麼問題?或是製造更多問題?](https://medium.com/fcamels-notes/test-driven-development-%E8%A7%A3%E6%B1%BA%E4%BA%86%E4%BB%80%E9%BA%BC%E5%95%8F%E9%A1%8C-%E6%88%96%E6%98%AF%E8%A3%BD%E9%80%A0%E6%9B%B4%E5%A4%9A%E5%95%8F%E9%A1%8C-937db879f695)
[TDD 開發五步驟](https://tw.alphacamp.co/blog/tdd-test-driven-development-example)
---