如何測試 event 裡提到 event 的測試有兩個方面,其中之一是測試 event 是否有被正確 trigger,有些 test framework 有檢驗 event 是否 trigger(或稱 emit)的驗證。(我應該是在某個 js test framework 看到的)

現在工作是用 Yii 這套 PHP framework,配合的 test framework 是 Codeception。

Yii2 有它的 Event 機制,看了下 Codeception 的 Yii2 module 沒有提供 event trigger 的驗證,上星期無聊就自己寫了個 codeception-yii2-event-tester 啦。

實作方面沒什麼困難,都在搞怎麼包 composer package 跟搞定 Travis。

Problem

Travis 在 run composer install 會出現類似以下錯誤:

1
2
3
4
5
Failed to clone the git@github.com:jquery/jquery-dist.git repository, try running in interactive mode so that you can enter your GitHub credentials

[Composer\Repository\InvalidRepositoryException]
No valid bower.json was found in any branch or tag of https://github.com/jq
uery/jquery-dist.git, could not load a package from it.

失敗的 repository 不一定,可能這次 jquery 下次別的。

找了一陣,說是踩到 Github 的 rate limit(我理解是 composer install 會一直從 github 抓東西所以容易踩到),但又有文章說這個問題已經被修正。我還是踩到了啊

Solution

  1. 在 Github 產生 Personal access token
  2. 在 Travis 設定有使用 Travis 的 project 的環境變數(environment variable),指定變數名稱,值是剛剛在 Github 產生的 access token。
  3. 修改 .travis.yml,在 composer install 前加入 composer config github-oauth.github.com ${環境變數名稱}

Ref

測試 event 分成兩邊:

  • 測試 event listener:有沒有在 event 發生後做該做的事。
  • 測試 trigger event:有沒有正確 trigger event。

基本概念是驗證一方時假造另一方。

測試 event listener

檢查 event 有沒有註冊到 event listener。用「event 發生後 event listener 有沒有做該做的事」來驗證,而不是試圖 access class 的內部 event 資訊來看是否有註冊成功。例如發生 event 後系統某些狀態會改變,測試方式是在測試裡 trigger event(假造 trigger event),驗證系統狀態是否有正確改變。

如果發生 event 後會 call 某些 third party function,測試方式是先做個 mock object、inject mock object 到被測試 class,接著 trigger event,最後驗證 mock object 是否有被 call 到該 call 的 function。

測試是否有 trigger event

在測試中給要測試的 event 註冊一個假的 event listener,接著讓被測試 class 做應該要 trigger event 的事情,最後驗證假 event listener 有沒有被 call 到。

驗證假 event listener 有沒有被 call 到不一定要用 mock object,也可以是假 event listener 在被 call 到時修改 test 裡的變數,最後直接驗證該變數,這跟語言支援有關。

test framework 支援

有些 test framework 的支援「某個 event 是否有被 trigger 或 emit 的驗證」。

Download kernel source code

我用的 kernel source code 是從 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git clone 的,寫這篇的版本是 5.0.0-rc3

1
$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git linux-stable

Add system call

首先在 kernel source code root 建立一個資料夾 mysyscall/

1
2
$ cd linux-stable
$ mkdir mysyscall

新增 mysyscall/hello.c、加入新的 system call sys_hello()(不可免俗的來 hello world 一下):

mysyscall/hello.c
1
2
3
4
5
6
#include <linux/kernel.h>

asmlinkage long sys_hello(void) {
printk("Hello Kernel World!\n");
return 0;
}
(More...)

Extract and Override 是另一種 injection 方式,它幾乎不會改變程式的語意(增加 constructor 的參數或者其他 public 介面等等),寫起來乾淨漂亮。它適合用於模擬回傳值或回傳 stub 或 mock object,不適合用在確認被測試程式與 dependency object 的互動。

Override factory method to inject stub

  1. 在被測試 class 加入可被繼承並 override 的 factory method 來取得 dependency object。
  2. 在測試裡新增一個 class 繼承被測試 class,override 該 factory method 回傳 stub object,接著使用測試 class 進行測試。
(More...)

接下來要 inject dependency object 啦~

Constructor & Setter Injection

Constructor Injection

在被測試 class 加新的 constructor 或在原本的 constructor 加新參數,傳進剛抽出來的 interface 的 object,將它存在被測試 class 的 member,被測試 class 裡的程式邏輯使用這個 member 做事。

(More...)

External Dependency 是系統中與被測試程式互動但你無法控制的物件。被測試程式受到 external dependency 行為影響可能有不同結果,為了保持 unit test 穩定,不會一下結果該是 A、一下結果該是 B,我們希望能掌控 external dependency──藉由 stub object 模擬 external dependency 的行為並將其 inject 到被測試程式中,基本步驟如下:

  1. 抽出 external dependency object 的 interface
  2. stub implement 該 interface 並實作 function
  3. inject stub 到被測試程式
    • Constructor Injection
    • Setter Injection
    • Extract and Override

本篇用例子說明步驟 1 跟 2:MyClass 是被測試程式,Foo 是 external dependency。(為了讓 code 短一點直接在 header 實作)

(More...)

External Dependency 是系統中與被測試程式互動但你無法掌控的物件。互動就是有 call 啊、使用回傳值之類的。

stub 是在系統中產生一個可以控制的替代 object 來取代 external dependency object。

使用 stub 可以解決直接相依帶來的測試問題:無法控制相依物件的行為及回傳值(例如每次 call third party API 得到的結果不同)或者相依物件不穩定,而難以有穩定的環境(固定的 input 及 output)測試要測試的程式邏輯。

一種典型的 stub 是回傳假資料,藉由假造不同的回傳值來測試程式在不同情境下的運作,例如假造其他 function 的各種可能的回傳值。

Build & Debug C++

安裝 extension C/C++ (ms-vscode.cpptools)。

Build

ctrl + ship + p 輸入 tasks,選擇 Configure Task,選個 template 來改。或者直接在 .vscode/ 下新增 tasks.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make",
"args": [
],
"group": "build",
"presentation": {
"reveal": "silent"
},
"problemMatcher": [
"$gcc"
]
}
]
}

這是有 Makefile 的設定方式,command 也可以用 g++ 配合 args

Debug

左邊切到 Debug 按上面的齒輪可以設定 launch.json,也可以直接在 .vscode/ 下新增設定檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/a.out", // 指定執行檔
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "build" // debug 前要執行的 task, 對應 tasks.json 的 taskName
}
]
}

設定好之後按 F5 就可以 Debug 囉~

breakpoint 只要在 code line number 左邊按出紅點點就可以啦~(g++ 要記得 -g

Extension

workspace 推薦 extension

.vscode/ 底下加入 extensions.json,可以在裡面列推薦跟不推薦的 extension。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp

// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-vscode.cpptools",
"donjayamanne.githistory"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [

]
}

Git History

ctrl + shift + p 後打 git h 可以看 git log、file 跟 line history。

選一個 commit 點下面的修改檔案還可以看 diff。

Sublime Text Keymap and Settings Importer

如果習慣 sublime 的 keymap 可以用這個 extension,其他還有 Visual Studio、Eclipse 等等的 keymap。

Troubleshooting

檔案太多 vscode 無法 watch changes

這裡說可以修改 /etc/sysctl.conf,加上:

1
fs.inotify.max_user_watches=524288

然後下 sudo sysctl -p load 進設定。

也可以設定 vscode 的 files.watcherExclude 來 exclude 一些不想 watch changes 的 folder。

Ref

NUnit 是 .NET 的 unit test framework

建立專案

  1. 新增「Visual Studio C# 類別庫 (.Net Framework)」專案
  2. 在 solution 裡加入 unit test 新專案,一樣是 Visual Studio C# 類別庫 (.Net Framework)

unit test project 可命名為 [Project].UnitTests

安裝 NUnit 套件

在 unit test project 右鍵 → 管理 NuGet 套件 → 搜尋 → 安裝。

安裝 NUnitNUnit3TestAdapter 套件,NUnit 裝完可以在參考看到 nunit.framework

安裝的 NUnit 版本是 3.11.0。

寫 & 跑測試

在 unit test project 加入要測試的 project 的參考。

在 class 前標註 [TestFixture] 表示 NUnit 測試的類別,在 function 前標註 [Test] 表示測試。

選單→測試→執行→所有測試,就會出現「測試總管」顯示測試結果啦~

(More...)