---
# System prepended metadata

title: 三之 1、 JS 102 測試方法
tags: [2020七月第五週, Lidemy心得, JS102, JavaScript續, 進度筆記]

---

# 三之 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 :  
![](https://i.imgur.com/LZzkY0N.png)

---

## 測試與 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 ， 成功後會看到:

![](https://i.imgur.com/8NGnL70.png)

---

## 套用 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 修改:

![](https://i.imgur.com/RDyAZWe.png)
在 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);
});
```
![](https://i.imgur.com/ZNGIWAq.png)

用指令 `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);
});
```
![](https://i.imgur.com/uCLQBNr.png)


我把 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` 指令可以看到:

![](https://i.imgur.com/nqMGMde.png)

---

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

![](https://i.imgur.com/Swfrn1j.png)

中文敘述顯示亂碼的話要套用其他套件。

---

## 可以多來幾個 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('');
});

```

![](https://i.imgur.com/A1NtPNq.png)


一個 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 的群組下。  

![](https://i.imgur.com/xTKzcis.png)

---

# 三之 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)

---