[實作筆記] Unit Testing With TypeScript

前情提要

  • 難得有機會寫前端的東西
  • 其實只有寫 JavaScript
  • 我要用 TypeScript 寫
  • 合理的軟體工序 TDD
  • 所以我要寫測試
  • node 版本 v8.11.1
  • npm 版本 5.6.0
  • TypeScript 版本 3.3.3333

Context & User Stories

來自前端的需求,在一個日曆工具要加入對可選日期判斷的邏輯。
原始需求如下,g、h、i、j 可以透過修改日曆元件選項調整,
而細微的日期與時間判斷需要撰寫新的方法作判斷。

原始需求

a. 每週一過中午 12 點不能選週二及以前的日期
b. 每週二過中午 12 點不能選週三及以前的日期
c. 每週三過中午 12 點不能選週四及以前的日期
d. 每週四過中午 12 點不能選週五及以前的日期
e. 每週五過中午 12 點不能選隔週一及以前的日期
f. 每週日都不能選
g. 90 天以後的日期不能選
h. 需指導我們如何讓特定日期不能選,以因應遇到國定假日的狀況
i. 預設為選擇最近一個可以使用的日期
j. 改成中文

測試環境準備

使用 Mocha 與 Chai

1
npm i -D chai mocha nyc ts-node typescript

安裝 TypeScript

安裝至專案

1
>$ npm i -D typescript ts-node

安裝至全域

1
>$ npm i -g typescript

建立專案 tsconfig.json

1
>$ tsc --init

安裝 MochaJs

1
npm i -D mocha @types/mocha

安裝 Chai

1
npm i -D Chai @types/chai

設定測試

package.json

1
2
3
"scripts":{
"test": "mocha -r ts-node/register tests/**/*.test.ts",
}

寫測試

1
2
3
4
5
6
7
8
9
import { expect } from 'chai';
import Calculator from '../src/calculate';

describe('calculate', function() {
it('add', function() {
let result = Calculator.Sum(5, 2);
expect(result).equal(7);
});
});

實作代碼

1
2
3
4
export default class calculator {
static Sum(a: number, b: number): number {

}

執行測試

1
npm t

修正代碼

1
2
3
4
5
export default class calculator {
static Sum(a: number, b: number): number {
let c = a + b;
eturn c;
}

測試結果

1
2
3
4
5
calculate
√ add


1 passing (61ms)

測試覆蓋率

// TODO

安裝 nyc

1
npm i -D nyc

設定 scripts

1
2
3
"scripts":{
"testCover": "nyc -r lcov -e .ts -x \"*.test.ts\" mocha -r ts-node/register tests/**/*.test.ts && nyc report",
}

執行

1
> npm run testCover

執行結果

1
2
3
4
5
6
7
8
9
10
11
12
略過測試部份
------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 98.63 | 100 | |
src | 100 | 100 | 87.5 | 100 | |
Calculator.ts | 100 | 100 | 100 | 100 | |
beforeShowDay.ts | 100 | 100 | 80 | 100 | |
tests | 100 | 100 | 100 | 100 | |
beforeShowDay.test.ts | 100 | 100 | 100 | 100 | |
calculator.test.ts | 100 | 100 | 100 | 100 | |
------------------------|----------|----------|----------|----------|-------------------|

完整 Test Cases

今天是 2019/3/30 號星期六 12:05
√ 日曆上 2019/4/02 星期二 出貨 可以選
√ 日曆上 2019/4/02 星期二 設定為國定假日, 出貨 不可以選

今天是 2019/3/24 號星期日 12:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/23 號星期六 12:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/22 號星期五 23:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/21 號星期四 01:30
√ 日曆上 2019/3/21 星期四 出貨 不可以選
√ 日曆上 2019/3/22 星期五 出貨 可以選

今天是 2019/3/22 號星期五 23:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/19 號星期二 23:05
√ 日曆上 2019/3/21 星期四 出貨 可以選

今天是 2019/3/22 號星期五 12:59
√ 日曆上 2019/3/22 星期五 出貨 不能選,因為現在時間超過 12 點
√ 日曆上 2019/3/25 星期一 出貨 不能選,因為現在時間超過 12 點
√ 日曆上 2019/3/26 星期二 出貨 可以選
√ 日曆上 2019/4/1 星期一 出貨 可以選
√ 日曆上 2019/3/23 星期六 出貨 不能選,因為現在時間超過 12 點

今天是 2019/3/21 號星期四 23:59
√ 日曆上 2019/3/21 星期四 出貨 不能選,因為現在時間超過 12 點

今天是 2019/3/18 號星期一 12:00
√ 日曆上 2019/3/18 星期一 出貨 不能選,因為現在是 12 點
√ 日曆上 2019/3/19 星期二 出貨 不能選,因為現在是 12 點
√ 日曆上 2019/3/20 星期三 出貨 可以選
√ 日曆上 2019/3/21 星期四 出貨 可以選

今天是 2019/3/18 號星期一 10:00
√ 日曆上 2019/3/24 星期日 出貨;不能選,因為週日都不能選
√ 日曆上 2019/3/18 星期一 出貨 不可以選,因為當天不能選
√ 日曆上 2019/3/19 星期二 出貨 可以選
√ 日曆上 2019/3/20 星期三 出貨 可以選

心得

能堅持「工序」是專業人士的表現,
我相信這是一種實務上能保持品質與速度的作法。
特別是越大規模越複雜的專案,
避免掉進焦油坑的方法就是一開始就別踩下去。

寫測試案例也是一種技能,在這次的 Case 中,
E2E 測試是相對困難的,受限於時間與日期,
單元測試可以控制時間反而成了絕妙的工具。

我一開始寫的測試並不優良,涵蓋的情境不夠(註:這裡並非指程式碼的函蓋率),
但是與 PO 反覆確認之後,調試出的情境終於滿足了需求,
看來我還要增進一下寫測試案例的能力。

最後,下次再試試用 Jest 寫寫看。

參考

(fin)

Please enable JavaScript to view the Gitalk. :D
Please enable JavaScript to view the LikeCoin. :P
Please enable JavaScript to view the LikeCoin. :P