文章目錄
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 順序
    2. 2.2. 該測什麼?
  3. 3. 痛點
  4. 4. 結論

這是我蠻常被問到的問題。想想也蠻有趣的,有不少是後端或者不太知道前端做些什摸樣的事情,然後、聽到寫測試,覺得很奇妙就問了這樣。相較於很多人分享測試環境該怎摸設定,用哪一套比較好,或者應該要 TDD 還是 BDD ,這篇想說的都不是這些,只是想描述下、前端測試要寫哪些東西、又該注意些什摸這樣。

前言

搜尋下測試相關的文章,應該蠻容易看到測試的各種好處。對我來說、也是好處大過麻煩。至於對我來說 TDD 還是 BDD ,我只能說、從有想法的那個地方開始就是好的開始,如果因為被 TDD (或 BDD )打動,但是有一個目的很明確的功能要實作卻不知道如何開始,那還不如以自己拿手的為出發點開始。

然後、如果是自己的 spec 自己寫的話,我覺得還有個很重要的心態,那就是要以旁觀者的角度來看自己的程式。如果、完全沈浸在一邊看 code 、一邊寫 spec,然後覺得 code 的東西都是對的,那就慘了!我想、這某方面也是 TDD 的好處,一般來說、如果 spec 是後來加上去的,那好像真的蠻容易犯這樣的錯誤。但是、我是認為,不可能單方面的寫完某一邊,另一邊就要完全遵從啦~!畢竟、一個功能的完善,都是反覆的修正,所以、我才會認為,從哪一方面開始並不是很重要,重要的是、必須以正確的心態,然後反覆的修正(有點夾擊的意味在…),然後達到最後的目的。

如果是寫他人的 spec ,也不行因為看得懂對方的程式,就認為對方的程式一定是對的,要跟他的結果一樣,這樣反而無法達到寫測試的目的,而且、有些問題也可能真的就是大家的認知不同,造成不同的結果,這也都是很好、可能很需要討論的問題,如果錯失了也太可惜了。

正文

內容大概包含從寫測試的順序到該測什摸。

順序

我習慣的方式大概是從 data 部分開始測(現在比較大的 project 應該大部份都 mvc 了,就從資料部分開測),然後小 component 到大的。採用這順序的理由是:

  • 如果資料處理的地方有問題,那… 能確保 component 真的是對的嗎?
  • 資料處理有沒有正確、真的很重要。
  • 大的 component 都是小的兜出來的, data flow 也是串接各種小 component。從大的測、確定小的沒問題嗎?

基於上述的理由,所以、大致上會採用這樣的方式,當然、很單純的就無所謂了啦~!

該測什麼?

先來寫個不太完整、但有個大概的 code 當被測範本。

定義 class A

1
2
3
4
5
6
7
8
9
10
11
class A {
constructor() {
this.state = {
// ...
};
}
method1() {}
method2() {}
}

定義 class B ,然後、B 裡面會用到 A 的 instance。大概是這個關係。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class B {
constructor(a) {
this.state = { a };
}
method3() {
this.state.a.method1();
}
method4() {
this.state.a.method2();
}
render() {
return (<div>BBB</div>);
}
}

首先、從順序上來說,就會先測 A ,這樣可以在測 B 的時候,不會擔心 A 的 method 有問題,所以、先來看 A 的測試。

接著,要測什摸、該測到多詳細,這取決於個人的期待,但是我大概是循序的依照這些情況做的。

  1. 要測的東西存不存在。
    • 有時候會是 path 問題什摸的。或者改名字了。
  2. 初始化有沒有正確。
    • 該具備的 state、method 有沒有都存在,理由同上。
  3. 按照順序把 method 測一遍。
    • 如果有的 method 裡面有用到 if-elseswitch 或者其他比較複雜的判斷,都可以多拆幾個情況試試。
      • truth table 其實蠻好用的。
      • 狄摩根也很好。
    • method 的順序也可以跟 component 一樣、由小到大。確保使用小 method 的時候沒有問題。
  4. 有 event 的話、要測 event。
  5. 有 render 的話、要測 render。
    • 初始化。
    • 初始參數可自訂。
    • 操作後的 render 變化。

4, 5 都是不一定會有的。然後、在現在 template 和 js 綁得比較緊的情況,event 沒有測也還好,但是 unit test 一定要好好寫。

我想 3. 應該是比較有難度的(?),就直接看看 3 就好囉~!

1
2
3
4
5
6
7
8
// 先測 A.method1,A.method2 是好的
it('A.method1 work', () => {
// ...
});
it('A.method2 work', () => {
// ...
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 接下來測 B
it('B.method3 work', () => {
const a = new A();
const b = new B(a);
a.method1 = jasmine.createSpy();
b.method3();
expect(a.method1).toHaveBeenCall();
});
it('B.method4 work', () => {
const a = new A();
const b = new B(a);
a.method2 = jasmine.createSpy();
b.method3();
expect(a.method2).toHaveBeenCall();
});

從上面 B test 會發現、我在測 method3method4 的時候,對於用到的 a.method1a.method2 只去檢查他有沒有被使用到。原因很簡單,因為 A 的測試已經檢查過。我們必須確保在 A 的時候、他的 method 都是對的。因此、在 B 的時候,就不用再把 A 的所有情況檢查一次,只需要檢查有沒有正確使用(像是、有沒有被呼叫,然後參數有沒有正確…),這樣就可以了。

同樣的方式、在檢查 render 或 component 的時候也是這樣。只要小 component 沒有問題,那摸、大 component 就只需要確保參數無誤、有包含小 component 就可以了。

痛點

還是有一些東西、我覺得不是很好寫,或者是沒有辦法進入測試的。

  • redirect:如果有用到 locaion.href... 的話,大概都會沒辦法過啦~ 因為測試是不能導到其它頁面噠,path 大概也都不會對。
  • httpRequeset:他需要另外處理 rejectresolve,覺得有這個的寫起來都有點麻煩。
  • timeout:千萬不要傻傻得設那 setTimeout ,扣掉時間不會準以外,可能還會讓測試的時間拉長,我想大部份應該都有提供假的 time tick 歐~。但是、太多的 timer 我認為還是不好啦~ 會比較容易混亂,當 project 很大或者比較複雜的時候。
  • 千萬不要以為 backend 改了什摸、 frontend 就會知道了,不管是 schemema 還是 api 改了, frontend 是絕~~~對不會知道的。

結論

不管寫的是哪種測試,我覺得大概都是 input 、 output 要掌握好。要寫到多詳細、就看需要,有些完全不可能發生的事情、我認為就算不測也沒有問題。然後、陸續寫過基於 xxx framework 的程式,倒是大同小異。覺得最大的差異大概就是 event 和 renderer 好不好測的差別了~。

關於其他的眉眉角角、也許可以再多開幾個小篇來談談。(等醞釀好的時候~)

文章目錄
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 順序
    2. 2.2. 該測什麼?
  3. 3. 痛點
  4. 4. 結論