[TOC]
# J_Vanilla JS 測試心路歷程
## 前言
寫完專案進入空窗期,因為看到前輩的面試分享,內容有提到「單元測試」,且其實切版 t7.1 裡,也有提到要對功能做單元測試:
> 就在這!!

所以趁現在有 to do list 這種小型專案,來寫寫看測試吧~
## Vanilla JS Unit Test
JS17 中,我們寫過了許多單元測試,即是將邏輯利用 function 獨立出來做測試,我可以理解 to do list 中有純邏輯的部份(例如:排序)。咦?結果就只找到排序是純邏輯,這樣只寫一個測試不合理啊,所以我去 Google 到了這篇神Q所寫的文章:
[Unit Test | 香草 JS 的單元測試 feat. @testing-library/dom](https://medium.com/starbugs/unit-test-%E9%A6%99%E8%8D%89-js-%E7%9A%84%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6-feat-testing-library-dom-f8539137bec9)
稍微理解了,其實 DOM 也可以做測試,內文應該主要在講測試 function 時,如果遇到需要 DOM 的狀況,就補 DOM 給他,讓其不會報錯,就能專心測邏輯了~
到這邊我想起了曾經與 Chris 聊過的測試話題,寫測試的目的,其實不只是要證明程式碼可以運行,主要是要展示 function 的極限跟使用方法。
Chris 也常常提到,測試要寫到什麼程度,就是取決於什麼情況會被客訴,在測試中寫到的所有狀況,都可以算是「正常情況」,也就是說,就算 function 出錯了,如果你在測試中有寫道,在某某情況 function 是會報「某某」錯的,而使用者也確實有這樣使用,那就屬於「預期內」的狀況,就不是「異常狀況」。
所以我也理解為,function 的「使用說明」,怎麼用這個 function 會吐出什麼內容;怎麼用 function 會出錯等等...
想完這些,就覺得測試也可以寫得非常冗長,因為我認為「畫面」也很有測試的必要,告訴使用者,哪個 function 是點擊某某元件觸發的,觸發後會如何調整 DOM,在畫面上會如何呈現才是預期的,又當資料來時,畫面有沒有長出對應的項目,且項目內的每個欄位中,有沒有填入應該項目的資料等等...
未免想太多了!裝上 jest 先跑一個試試看:
```javascript
// initTabsSwitching.js
export function initTabSwitching(liList) {
liList.forEach((li) =>
li.addEventListener("click", function () {
liList.forEach((li) => li.classList.remove("active"));
this.classList.toggle("active");
})
);
}
```
這段是在對指定 li list 中的`<li>`做事件綁定,當點擊某個`<li>`時,將該`<li>`的 class 加上 active ,其餘`<li>`的 active 都拿掉。
```javascript=
// initTabsSwitching.test.js
import { initTabSwitching } from "../initTabsSwitching.js";
describe("li click 行為測試", () => {
test("點擊第一個<li>,只有第一個<li>有 active", () => {
//Arrange
document.body.innerHTML = `
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
`;
const liList = document.querySelectorAll("li");
//Act
initTabSwitching(liList);
liList[0].click();
//Assert
expect(liList[0].classList.contains("active")).toBe(true);
expect(liList[1].classList.contains("active")).toBe(false);
expect(liList[2].classList.contains("active")).toBe(false);
});
}
```
結果代誌大條了:

大致是就是說 Jest 看不懂我寫的 import 語法
以往我都使用 ES module 來寫程式(import/export的方式),但 Jest 本身是使用 CommonJS 來寫,所以只認得 require/module.exports 寫法,所以解法一,就是將所有 ES module 寫法都改成 CommonJS 寫法,但我認為不太實際,現在是因為專案很小,而且都我自己寫的,要改可能工程不會太大,但如果今天是一整個團隊的案子,要改的部分可就不容小覷了,所以方法二,安裝 babel 來將 ES module 的不了字動轉譯成 CommonJS
## Babel
所以我們就照著官網來裝 babel:

```
npm install @babel/preset-env --save-dev
```
建立`babel.config.json`:
```json
{
"presets": ["@babel/preset-env"]
}
```
再跑一次試試看吧~

成功跑了測試,但`document is not defined`...
## jest-envioronment-jsdom
表示在node環境中是沒有 DOM 的,於是去 Jest 搜尋了 DOM 的測試:
[Jest-DOM Manipulation
](https://jestjs.io/docs/tutorial-jquery)
前面都在講用 jQuery 怎麼寫等等...
到最後看到這句關鍵,要裝`jest-environment-jsdom`來模擬 DOM 的操作~

```
npm install --save-dev jest-environment-jsdom
```
然後在測試範例中看到這段:

所以我們就在`package.json`中加上`testEnvironment`:
```json
{
...
"scripts": {
"test": "jest",
"test-coverage": "jest --coverage",
"watch": "sass --watch ./Jeremy/scss/main.scss ./Jeremy/css/main.css"
},
"jest": {
"testEnvironment": "jsdom"
},
...
}
```
終於成功啦!

接著就可以開始我們的 to do list 測試之旅了...
{"showTags":"false","title":"J_Vanilla JS 測試心路歷程","description":"寫完專案進入空窗期,因為看到前輩的面試分享,內容有提到「單元測試」,且其實切版t7.1裡,也有提到要對功能做單元測試,","contributors":"[{\"id\":\"ea951d67-eaab-46d0-b797-96b6bafbbfbc\",\"add\":3635,\"del\":140}]"}