Google Unit Test Framework試玩

一直一來我都知道Unit Test對於程式的開發是蠻重要的一個環節,在以往我們常常不停地改寫程式,在這個過程中,有意無意地常常會有東西被改壞掉還不知道,而我們檢查的方式可能很常是透過一直修改程式碼或輸入的資料來測試,但是當程式越來越大,我們測試時只專注在一個地方,此時所要測試的程式越來越多,不小心出錯的可能也越來越多,每改寫一次就是一次昂貴的人力檢查,最後程式就像一艘漏水的船,補了這裡,那裡卻又多了一個洞,船員像無頭蒼蠅一樣四處補洞,這樣的情況就是Unit Test能解決的問題,透過Unit Test,每次寫的程式碼都交給程式自動測試,一來測試的速度很快,不管是一百次還是一千次,總比人工測試來得快速、正確、廣範,但這不是免費的,代價就是程式設計師要寫很多Unit Test的程式碼,還要想要如何寫Unit Test的程式,進一步的還有測試導向的開發模式(Test-driven development),程式是在Unit Test寫完之後才開始寫,這樣一來迫使程式設計師在一開始就得把設計想好,邊寫邊想的做法不再可行,這或許算缺點也算優點

這些我都知道,但都沒有很確實去做,之前有用Python寫過Unit Test,他那種寫法我很喜歡,但C++卻沒有,直到這次想開發某些東西,我決定要試著落實TDD,但在開始以前,要有合適的工具,也就是自動測試的框架,起初我使用Boost.Test,雖然說可以使用,但是我不得不抱怨,他的文件是寫給鬼看的,好幾十頁的文件裡面廢話連篇,分類也很不明確,難以找到重點,或許是我英文程度太差,我真的讀不太懂Boost.Test到底怎麼用,靠著東拼西湊勉強終於還算可以使用,但是或許是我搞不太清楚它一堆Compile時用的Macro定義的Flag如何使用,編譯起來不知為何非常地慢

不合胃口(有些反胃的感覺)的文件,讓我覺得該是時候和Boost.Test說再見,尋找新歡的時候到了,無意間發現,在不知道什麼時候突然冒出來的Google Unit Test Framework,似乎才剛釋放出來沒多久,是Google內部C++寫Unit Test的框架,開放成Open source的專案,看起來很不錯,文件簡單明瞭,不像Boost.Test幾十頁連篇翻半天還不知道Unit Test到底要怎樣執行,他們主張為什麼要使用Google Unit Test Framework的理由:

  1. Tests should be independent and repeatable. It’s a pain to debug a test that succeeds or fails as a result of other tests. Google C++ Testing Framework isolates the tests by running each of them on a different object. When a test fails, Google C++ Testing Framework allows you to run it in isolation for quick debugging.
  2. Tests should be well organized and reflect the structure of the tested code. Google C++ Testing Framework groups related tests into test cases that can share data and subroutines. This common pattern is easy to recognize and makes tests easy to maintain. Such consistency is especially helpful when people switch projects and start to work on a new code base.
  3. Tests should be portable and reusable. The open-source community has a lot of code that is platform-neutral, its tests should also be platform-neutral. Google C++ Testing Framework works on different OSes, with different compilers (gcc, MSVC, and others), with or without exceptions, so Google C++ Testing Framework tests can easily work with a variety of configurations. (Note that the current release only contains build scripts for Linux – we are actively working on scripts for other platforms.)
  4. When tests fail, they should provide as much information about the problem as possible. Google C++ Testing Framework doesn’t stop at the first test failure. Instead, it only stops the current test and continues with the next. You can also set up tests that report non-fatal failures after which the current test continues. Thus, you can detect and fix multiple bugs in a single run-edit-compile cycle.
  5. The testing framework should liberate test writers from housekeeping chores and let them focus on the test content. Google C++ Testing Framework automatically keeps track of all tests defined, and doesn’t require the user to enumerate them in order to run them.
  6. Tests should be fast. With Google C++ Testing Framework, you can reuse shared resources across tests and pay for the set-up/tear-down only once, without making tests depend on each other.

聽起來很合我胃口,於是就下載來試試,首先,要得編譯Google Test的library,以Visual Studio來說,開啟它的.sln檔,然後編譯就可以了

接著是如何使用Google Test? 雖然有點小麻煩,但其實也還好,首先include “gtest/gtest.h”,接著是lib的設定,因為Google Test的lib是使用/MT參數進行編譯,所以你的專案也一樣要設為/MT或/MTd(Debug 模式下使用),然後link gtest.lib就可以了,如果不這樣做,你會看見一大堆xxx aleardy defined in xxx的link錯誤

而我用Code Blocks似乎因為編譯參數不太一樣又遇到一堆link錯誤,後來發現好像是Code Blocks的Console Application專案一開始就link的msvcrtd.lib一系列.lib所造成的,將那些移除只剩gtest.lib或gtestd.lib就可以正確執行,我隨手從它的範例裡組合出一個測試的執行檔

#include

#include

using namespace std;

// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
int Factorial(int n) {
  int result = 1;
  for (int i = 1; i <= n; i++) {
    result *= i;
  }

  return result;
}

// Returns true iff n is a prime number.
bool IsPrime(int n) {
  // Trivial case 1: small numbers
  if (n <= 1) return false;

  // Trivial case 2: even numbers
  if (n % 2 == 0) return n == 2;

  // Now, we have that n is odd and n >= 3.

  // Try to divide n by every odd number i, starting from 3
  for (int i = 3; ; i += 2) {
    // We only have to try i up to the squre root of n
    if (i > n/i) break;

    // Now, we have i <= n/i < n.
    // If n is divisible by i, n is not prime.
    if (n % i == 0) return false;
  }

  // n has no integer factor in the range (1, n), and thus is prime.
  return true;
}

// Step 2. Use the TEST macro to define your tests.
//
// TEST has two parameters: the test case name and the test name.
// After using the macro, you should define your test logic between a
// pair of braces.  You can use a bunch of macros to indicate the
// success or failure of a test.  EXPECT_TRUE and EXPECT_EQ are
// examples of such macros.  For a complete list, see gtest.h.
//
//
//
// In Google Test, tests are grouped into test cases.  This is how we
// keep test code organized.  You should put logically related tests
// into the same test case.
//
// The test case name and the test name should both be valid C++
// identifiers.  And you should not use underscore (_) in the names.
//
// Google Test guarantees that each test you define is run exactly
// once, but it makes no guarantee on the order the tests are
// executed.  Therefore, you should write your tests in such a way
// that their results don't depend on their order.
//
//

// Tests Factorial().

// Tests factorial of negative numbers.
TEST(FactorialTest, Negative) {
  // This test is named "Negative", and belongs to the "FactorialTest"
  // test case.
  EXPECT_EQ(1, Factorial(-5));
  EXPECT_EQ(1, Factorial(-1));
  EXPECT_TRUE(Factorial(-10) > 0);

  //
  //
  // EXPECT_EQ(expected, actual) is the same as
  //
  //   EXPECT_TRUE((expected) == (actual))
  //
  // except that it will print both the expected value and the actual
  // value when the assertion fails.  This is very helpful for
  // debugging.  Therefore in this case EXPECT_EQ is preferred.
  //
  // On the other hand, EXPECT_TRUE accepts any Boolean expression,
  // and is thus more general.
  //
  //
}

// Tests factorial of 0.
TEST(FactorialTest, Zero) {
  EXPECT_EQ(1, Factorial(0));
}

// Tests factorial of positive numbers.
TEST(FactorialTest, Positive) {
  EXPECT_EQ(1, Factorial(1));
  EXPECT_EQ(2, Factorial(2));
  EXPECT_EQ(6, Factorial(3));
  EXPECT_EQ(40320, Factorial(8));
}

// Tests IsPrime()

// Tests negative input.
TEST(IsPrimeTest, Negative) {
  // This test belongs to the IsPrimeTest test case.

  EXPECT_FALSE(IsPrime(-1));
  EXPECT_FALSE(IsPrime(-2));
  EXPECT_FALSE(IsPrime(INT_MIN));
}

// Tests some trivial cases.
TEST(IsPrimeTest, Trivial) {
  EXPECT_FALSE(IsPrime(0));
  EXPECT_FALSE(IsPrime(1));
  EXPECT_TRUE(IsPrime(2));
  EXPECT_TRUE(IsPrime(3));
}

// Tests positive input.
TEST(IsPrimeTest, Positive) {
  EXPECT_FALSE(IsPrime(4));
  EXPECT_TRUE(IsPrime(5));
  EXPECT_FALSE(IsPrime(6));
  EXPECT_TRUE(IsPrime(23));
}

int main(int argc, char** argv) {
  // Prints elapsed time by default.
  //testing::GTEST_FLAG(print_time) = true;

  // This allows the user to override the flag on the command line.
  testing::InitGoogleTest(&argc, argv);

  return RUN_ALL_TESTS();
}

很簡單明瞭吧? 然後它執行的結果畫面如下

Google test 執行結果

Google test 執行結果

沒想到還有顏色,看起來蠻酷的對吧,很酷有時候也是選擇專案的理由之一,在這樣試玩下來,雖然還沒深入研究,還有實際寫Unit Test,但是感覺起來真的很不錯,有興趣的人可以試一試

This entry was posted in C/C++, 中文文章, 分享 and tagged , , , , . Bookmark the permalink.

7 Responses to Google Unit Test Framework試玩

  1. Shen says:

    不錯的分享文! ^^b

  2. ReDrAy says:

    boost::test 我有在用,事實上整個 code 的感覺跟 google unit test 差不多,不過的確文件上有些複雜,不知道是不是因為有些進階功能? 至於編譯方面可以靠連結 lib 檔來增加速度。

  3. Tommy says:

    CPPunit也滿多人在用的,我覺得還滿方便的
    還可以結合QT

  4. Pingback: Google Unit Test Framework : 米缸还有米

  5. dzr says:

    我也想使用CodeBlocks来结合Google Unit Test Framework。
    首先我使用VS2010编译了Google Test,并在VS2010中成功的使用了Google Test,但当我再转移到CodeBlocks来用时,也如你所说的出现了很多的LINK错误,说gtest.lib中无法解析外部某某某符号。
    我想请教您是如何通过的,thanks in advance!

    • lzy says:

      lnk的error code是多少呢
      我也遇到过
      然后查了下应该是创建工程的时候不对
      :)

  6. 感謝分享, 正好需要 ^^