最近更新: 2012-04-23

GJS - 使用 jsUnit

GJS 自帶 jsUnit 模組,可以讓編程人員針對自己所寫的 javascript 程式碼撰寫測試案例。 這個 jsUnit 模組實際上就源自網頁程式開發人員使用的 JsUnit。 不過基於 GJS 的 server-side script 運作方式,GJS 自帶的 jsUnit 模組做了些調整,啟用 jsUnit 的方式也略有不同。

這篇文章簡單地說明 GJS 如何使用 jsUnit 。這些內容也同樣適用於 GNOME Shell 。

方式一

將 jsUnit 模組掛載到指定的名稱空間。故使用 jsUnit 提供的方法時,必須指定名稱空間。 慣例上,將掛載 jsUnit 模組的空間取名為 jsunit

#!/usr/bin/gjs
var jsunit = imports.jsUnit;

function test_true() {
    jsunit.assertTrue(1 == 1);
}

function test_true_failed() {
    jsunit.assertTrue("This is a failure.", 1 == 2);
}

jsunit.gjstestRun(window, jsunit.setUp, jsunit.tearDown);

gjstestRun 是 GJS 特地增加的測試案例執行函數。 如無特殊需求,通常把 gjstestRun 放在測試程式碼的最後一行,這個函數的回傳結果就會成為測試程序的執行結果。

在 gjs 環境中,我們不能省略 gjstestRun 的目標參數(第一個參數)。 就算我們的測試項目就位於全體作用域(window),仍然要把全體作用域作為參數傳入。

在第一種使用方式中,也不能省略 setUp 參數(第二個)與 tearDown 參數(第三個)。 如果我們不打算使用這兩個參數,就指定 jsUnit 自帶的那兩個傳入。jsUnit 自帶的實際是空函數。

在命令列執行之結果如下:


$ gjs jsunit-example1.js

$ echo $?
1

這個測試案例有一個失敗項目,所以程序執行結果($?)必不為 0 。在 Unix 家族中,程序執行結果不為 0 時,皆表示執行過程發生錯誤或失敗狀況。

方式二

將 jsUnit 提供的函數載入到全體作用域(window),使用時就不必加上空間的名稱。

#!/usr/bin/gjs 
const _Lang = imports.lang;
_Lang.copyProperties(imports.jsUnit, window); // 將指定空間的屬性全部複製到全體作用域。

function test_true() {
    assertTrue(1 == 1);
}

function test_true_failed() {
    assertTrue("This is a failure.", 1 == 2);
}

gjstestRun(window);

當我們將 jsUnit 載入到全體作用域時,測試程式碼的寫法近似於網頁開發人員使用 JsUnit 的寫法 - 不必為 assert* 函數前綴名稱空間。若我們不需要指定 setUp 與 tearDown 時,也可以省略 gjstestRun 的第二個與第三個參數。

執行方式與結果,同方式一。

沒有測試報告?

不論採用哪種方式啟用 jsUnit ,它都是採用靜默原則,不會輸出任何測試訊息。 我們只能從程序執行結果得知測試案例是全部通過或是有失敗項目。 當我們需要利用測試案例進行除錯時,這是很麻煩的特性。

若想要它輸出測試訊息,我們需要利用環境變數 GJS_DEBUG_OUTPUT 指定要除錯訊息的輸出位置。 我們只需要指定為 "stderr" 就可以在終端機上看到測試訊息了。

指定 GJS_DEBUG_OUTPUT 之後,命令列的執行結果將如下所示:


$ export GJS_DEBUG_OUTPUT=stderr
$ gjs jsunit-example2.js 2>&1 | grep LOG
    JS LOG: running test test_true
    JS LOG: running test test_true_failed
    JS LOG: "It is a failure."
    JS LOG: 1 tests failed in this file
    JS LOG: Failures were: test_true_failed

這下它的執行報告就方便我們收斂程式除錯的範圍了。

以 assert 取代 print 輸出除錯訊息

Whenever you are tempted to type something into a print statement or a debugger expression, write it as a test instead.

Martin Fowler
#!/usr/bin/gjs
const _Lang = imports.lang;
_Lang.copyProperties(imports.jsUnit, this);


function hello(msg) {
    /* type something into a debugger expression. */
    //if (!msg)
    //    throw new Error("You must pass a message.");
    
    /* write it as a test instead. */
    assertNotUndefined("You must pass a message.", msg);

    return "Hello " + msg;
}

print(hello());

註記

GJS / GNOME Shell 目前仍然嚴重缺乏使用文件。例如本文提到的 GJS_DEBUG_OUTPUT 的使用內容,就未出現在文件上。 GJS_DEBUG_OUTPUT 可以指定為一個實際文件路徑,例如 /var/log/gjs-debug.log ;或者用 "stderr" 這個關鍵字指示它輸出到標準錯誤設備。我不確定以後會不會變動。

jsUnit 的使用文件請參考: JsUnit Documentation 。可用的斷言請參考: JsUnit Assertion Functions

樂多舊網址: http://blog.roodo.com/rocksaying/archives/19360094.html

樂多舊回應
未留名 (#comment-22427618)
Tue, 24 Apr 2012 16:26:15 +0800
Yuren Ju 說:
> 你對 GJS 了解蠻多的耶,不考慮在 COSCUP 講一場相關主題的?
> 這個領域的講題在台灣很少
> 我看了一下在台灣有在關注我又認識的就只有你、我跟 Fred XD

我還沒去過 COSCUP 呢。原來 COSCUP 今年的活動又要展開了嗎?

server-side javascript 的領域,大部份焦點在 node.js 。
gjs 只算是 gnome-shell 的實驗性副產品,自然談的人就少了。

我對 gjs 所知道的內容,都在部落格上貼出了。我算了一下,內容並不多。

我先看看COSCUP公布的相關資訊,瞭解一下內容再來考慮。
yurenju@gmail.com(Yuren Ju) (#comment-22427968)
Tue, 24 Apr 2012 22:53:05 +0800
今年的邀稿還沒開始
不過應該快開始了~

我是覺得還蠻有趣的,只是比較少人關注
希望到時候可以聽到你的演講喔 :D
未留名 (#comment-22428614)
Wed, 25 Apr 2012 17:23:04 +0800
讓我想想 gjs 有什麼主題可講的...