【軟體工程】使用Google Test提升軟體品質:讓軟體測試覆蓋率提升超過80%

Google Test 是一個由 Google 提供的Unit Test Framework,專為 C++ 和 Java 項目而設。它提供了一系列的工具與函式庫,協助開發人員編寫和執行基本單元測試,以確保程式碼的正確性。

單元測試(Unit Test)是什麼?

單元測試是軟體開發過程中非常重要的一環,它的目的是確保程式碼符合預期的工作方式。以下是單元測試的一些主要原因:

  1. 確保代碼正確性:單元測試可以確保代碼是否正確地運作,並且是否符合預期的要求。這是因為測試可以模擬真實的使用情況,并確保代碼符合預期的工作方式。
  2. 更快地發現問題:當代碼更改時,單元測試可以幫助發現錯誤。這是因為測試可以自動執行,並立即顯示任何錯誤。這可以讓開發人員在早期就發現問題,並更快地解決它們。
  3. 提高代碼可維護性:單元測試可以提高代碼的可維護性。這是因為測試可以確保代碼正確性,並且可以幫助維護人員了解代碼的工作方式。這可以降低程式碼維護的成本,並確保代碼始終保持高品質。
  4. 提高開發效率:單元測試可以提高開發效率。這是因為它可以幫助開發人員快速發現問題,並且可以更快地解決問題。此外,單元測試也可以自動執行,因此開發人員不需要手動測試每個功能。這可以讓開發人員更加專注於開發新功能,而不必擔心現有代碼的正確性。
  5. 提高專案品質:單元測試可以提高專案的整體品質。這是因為測試可以確保代碼的正確性,並且可以更快地發現問題。這可以提高用戶對軟體的信任,並且確保軟體始終保持高品質。

單元測試是軟體開發過程中非常重要的一環,因為它可以幫助確保代碼的正確性,提高開發效率,提高專案品質,以及提高代碼的可維護性。通常我們進行軟體開發時,在每一次修改程式後都一定會使用單元測試確保在已存在的測試項目內能夠正常執行。

Google Test的使用範例

Google Test 是一種容易上手的單元測試解決方案,它提供了豐富的功能和特性,幫助開發人員更快地編寫和執行單元測試。以下將介紹一些實際使用範例。

檢測函式輸出數值

這個程式是一個使用Google Test測試一個加法function功能的範例。

#include <gtest/gtest.h>

int add(int a, int b) {
  return a + b;
}

TEST(AdditionTest, TwoPositiveNumbers) {
  EXPECT_EQ(add(2, 3), 5);
}

TEST(AdditionTest, TwoNegativeNumbers) {
  EXPECT_EQ(add(-2, -3), -5);
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

在此範例中,我們測試一個add function把兩個輸入的整數加在一起後回傳加起來的數值。

第7行的TEST(AdditionTest, TwoPositiveNumbers) 是 Google Test 中的一個測試用例。TEST Macro用於在 Google Test 中定義測試項目。第一個參數 AdditionTest 是測試套件的名稱。第二個參數 TwoPositiveNumbers 是測試項目的名稱。因此,TEST(AdditionTest, TwoPositiveNumbers) 定義了一個名為 TwoPositiveNumbers 的測試項目,屬於測試套件 AdditionTest。此測試項目檢測你的程式的某些部分,並驗證測試結果。這個名稱只是方便在測試結束後,Google Test可以回報有哪些測試通過以及哪些測試不通過的用途,因此這兩個變數命名的方式以自己方便為主。而在main()中使用::testing::InitGoogleTest(&argc, argv);來初始化整個測試,然後使用RUN_ALL_TESTS來進行所有測試項目。

這段程式使用TEST()這一個Macro定義了兩項測試,第一個是2+3是否等於5,以及第二個測試為(-2)+(-3)是否等於-5。在TEST()中使用EXPECT_EQ()來指明我們得到的數值和預期的數值是否相等。

檢測Array的內容

我們除了可以使用Google Test來測試數值是否相等以外,也可以使用它來檢測一個資料結構是不是我們預期的內容。例如以下是一個對於Array的測試。

#include <gtest/gtest.h>
#include <algorithm>

int arr0[] = {};
int arr1[] = {1, 2, 3, 4, 5};

TEST(ArrayTest, EmptyArray) {
  int size = sizeof(arr0) / sizeof(arr0[0]);
  EXPECT_EQ(size, 0);
}

TEST(ArrayTest, SortedArray) {
  int size = sizeof(arr1) / sizeof(arr1[0]);
  EXPECT_TRUE(std::is_sorted(arr1, arr1 + size));
}

在此範例中第7行開始的測試中,檢測一個輸入的Array是否是空的Array。如果arr0是空的,其size應該要是0。而在第12行開始的第二個測試項目中,我們想要檢測輸入的Array是否是一個已經排序好的Array,因此使用algorithm函式庫中的std::is_sorted函式來檢查arr1是否有排序了。

檢測Class物件的內容

Google Test也可以直接用來測試一個物件如以下這個例子。

#include <gtest/gtest.h>
#include <string>

class Person {
 public:
  Person(std::string name, int age) : name_(name), age_(age) {}

  std::string name() const { return name_; }
  int age() const { return age_; }

 private:
  std::string name_;
  int age_;
};

TEST(PersonTest, Name) {
  Person person("John Doe", 30);
  EXPECT_EQ(person.name(), "John Doe");
}

TEST(PersonTest, Age) {
  Person person("Jane Doe", 35);
  EXPECT_EQ(person.age(), 35);
}

在此測試當中,我們有一個Person的class。這段測試當中去檢測這個Person的物件是否有符合預期的名字以及年齡,這裡同樣是使用EXPECT_EQ來檢測是否相同。

Google Test的執行結果

假設我們設計了一個reverseString的函式用來反轉字串,並寫了三個測試項目,如以下程式範例。

#include <gtest/gtest.h>
#include <string>

std::string reverseString(std::string str) {
  std::reverse(str.begin(), str.end());
  return str;
}

TEST(ReverseStringTest, ReverseNormalString) {
  EXPECT_EQ(reverseString("hello"), "olleh");
}

TEST(ReverseStringTest, ReverseEmptyString) {
  EXPECT_EQ(reverseString(""), "");
}

TEST(ReverseStringTest, ReversePalindromeString) {
  EXPECT_EQ(reverseString("racecar"), "racecar");
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

當我們執行完此程式後,我們會得到以下輸出結果。

[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from ReverseStringTest
[ RUN      ] ReverseStringTest.ReverseNormalString
[       OK ] ReverseStringTest.ReverseNormalString (0 ms)
[ RUN      ] ReverseStringTest.ReverseEmptyString
[       OK ] ReverseStringTest.ReverseEmptyString (0 ms)
[ RUN      ] ReverseStringTest.ReversePalindromeString
[       OK ] ReverseStringTest.ReversePalindromeString (0 ms)
[----------] 3 tests from ReverseStringTest (0 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 3 tests.

此範例顯示全部三個ReverseStringTest的測試項目全都被執行且通過了。因此最後看到的結果是[ PASSED ] 3 tests. 如果有任何測試項目是FAILED的,則開發者可以知道要在針對哪一部份進行修改。

常使用的測試Macro

以下幾個是常被使用的測試macro。

  • EXPECT_EQ:用於測試兩個值是否相等。如果值不相等,該測試項目將顯示測試fail,輸出將顯示預期值和實際值。
  • EXPECT_NE:用於測試兩個值是否不相等。如果值相等,該測試項目將顯示測試fail,輸出將顯示相等的值。
  • EXPECT_TRUE:測試一個值是否為真。如果值為假,測試將顯示fail,輸出將顯示為假的值。
  • EXPECT_FALSE:測試一個值是否為假。如果值為真,測試將fail,輸出將顯示為真的值。
  • EXPECT_STREQ:用於測試兩個字串是否相等。如果字串不相等,測試項目將顯示fail,輸出將顯示預期字符串和實際字符串。
  • EXPECT_THROW:用於測試一個語句是否throw異常。如果語句沒有throw,測試用例將失敗,輸出將顯示沒有throw的語句。
  • EXPECT_NO_THROW:用於測試一個語句是否沒有throw異常。如果語句有throw,測試用例將失敗,輸出將顯示有throw的異常。

這些只是Google Test中的一小部分,完整的列表可以參考以下網頁中Google Test的完整文件。(http://google.github.io/googletest/


X. Ryan
X. Ryan

Hello!我是一個在矽谷工作,有軟體工程背景的量子計算科學家。這裡分享的內容主要是把平常研究開發時所用的小工具以及看過的東西記錄下來,同時也分享一些日常生活瑣事。

文章: 49

4 則留言

留言功能已關閉。