在寫程式的時候,我們不斷的重複著修改、測試、修改、測試...的流程。顯而易見的,我們要作不少重複的測試。在理想的情況下,當然是希望親愛的電腦可以替我們代勞:只要我說「請跑測試吧」,電腦就會進行測試,並且告訴我們哪個測試沒有通過。其實,這樣的好事不是天方夜譚,而是一個應該要應用到每個你心愛的程式上面的作業流程!
其中一種測試的方法,把整個程式切割成許多可以測試的小單位,然後依據這些小單位的功能進行一連串的測試。這樣的方式被稱作unit test。
在很多情況下,會有好心人士提供一套針對某個程式語言的unit test軟體。(如果沒有的話,考慮一下,寫一個吧!)。像是C#有NUnit, Java有JUnit, Haskell有HUnit...等。C語言的話,有check這個程式庫可以使用。
以fedora來說,可以用下面的方法來安裝check:
yum install check或者是看看自己的Linux distribution有沒有替你打包好。沒有的話Google找一下應該就有了(google check c)安裝完後,就可以開始使用了!基本上來說,在含有行unit test的程式碼中加上:
#include附帶一提,check.h中也包含簡短的使用說明,(個人認為相當的實用)請務必去看一下。
要編譯的時候,使用
gcc ... -lcheck來進行編譯。
講了那麼多,那要怎麼利用check來寫unit test呢?直接看範例好了。如果想要執行下面的範例,請記得
gcc test.c -lcheck
#include <check.h> #include <stdio.h> /* * 利用暴力法 * 計算start, start + step, start + step * 2 ... end的總和 */ int series_sum_ref(int start, int end, int step) { int s = 0; for(; start <= end; start += step) { s += start; } return s; } /* * 利用公式 * 計算start, start + step, start + step * 2 ... end的總和 */ int series_sum_formula(int start, int end, int step) { int n = (end - start) / step + 1; return n * (2 * start + (n - 1) * step) / 2; } /* * 所有的Unit test都要用START_TEST和END_TEST來包裹起來 */ START_TEST(test_series_ref) { fail_if(series_sum_ref(1, 10, 1) != 55); fail_if(series_sum_ref(1, 10, 2) != 25); fail_if(series_sum_ref(3, 5, 2) != 8); } END_TEST START_TEST(test_series_formula) { int a, b, s; for(a = -20; a <= -10; a++) { for(b = 0; b <= 10; b++) { for(s = 1; s <= 3; s++) { int r = series_sum_ref(a, b, s); int f = series_sum_formula(a, b, s); if(r != f) { printf("\nFail at a = %d, b = %d, s = %d\nr = %d, f = %d\n", a, b, s, r, f); fail("Test failed: the series sum from reference and formula are different."); } } } } } END_TEST /* * check的架構: * 可以設定許多組Suite, 每組Suite都有許多的TCase, * 每個Tcase又有許多test * 總之呢,這是一個階層式的架構。 * * Suite1 * __ TCase1 * _____ Test1 * _____ Test2 * ... * __ TCase2 * ... * __ TCase3 * ... * Suite2 * __ TCase2-1 * ... */ int main() { TCase* series_sum_case = tcase_create("Arith. Series"); tcase_add_test(series_sum_case, test_series_ref); tcase_add_test(series_sum_case, test_series_formula); Suite* suite = suite_create("Main Suite"); suite_add_tcase(suite, series_sum_case); SRunner* runner = srunner_create(suite); srunner_run_all(runner, CK_VERBOSE); int fail_count = srunner_ntests_failed(runner); srunner_free(runner); }
其實check有比這裡介紹的還要多些的功能。欲知詳情,可以看check.h喔!
另外,因為check是一個C語言相容的unit test framework,所以也可以用來測試任何C語言相容函數喔!(這包括Fortran, 組合語言等)
沒有留言:
張貼留言