單元測試框架:從零開始使用GoogleTest

2021-12-09 51Testing軟體測試網

在正式開啟GoogleTest之旅前,先介紹一點術語,以便平滑過渡。

所謂黑盒,將待測對象看成一個黑盒子,測試時不關心功能是如何實現的,僅關注輸入數據和實際輸出結果,核對實際輸出是否與預期輸出一致。

功能測試採用的就是黑盒測試技術,如下就是一條手機的功能測試用例,測試步驟對應輸入(點擊圖標)、預期結果對應輸出(打開應用)。

所謂白盒,簡單來說就是代碼層面的測試。測試人員需要了解功能是如何編碼實現的(需要讀懂代碼)。

單元測試就是代碼層面的測試,舉個例子。Absolute( )函數實現了求整數絕對值的功能:

int Absolute(int n)  {      if (n > 0)          return n;      else if (n < 0)          return n * -1;  } 

(左右滑動查看完整代碼)

瀏覽Absolute( )函數,可以看出它的代碼存在明顯bug:沒有考慮輸入為0的情況。

運行程序,當輸入數據為0時,輸出結果是錯誤的:

通過上面的示例,相信大家對單元測試有了一個比較直觀的印象了,下面給出單元測試的定義。

產品代碼中的 一個函數、一個類 或 一個接口均可以看成一個單元,針對這些單元的代碼級測試就是單元測試。

單元測試的對象是一個個「單元」,單元測試可以發現待測對象中的代碼級故障,對產品整體性錯誤無能為力。

雖然單元測試有其局限性,但是良好的單元測試可以保障一個單元模塊的代碼正確性,即:該單元被其他模塊調用時,自身是沒有代碼問題的。

GoogleTest 是Google公司開發的一款C++單元測試框架,Google Chrome瀏覽器使用的測試框架就是GoogleTest。

對於使用C++開發的產品,可以通過GTest編寫單元測試用例進行單元測試。

為什麼要編寫單元測試用例,上面的Absolute( )函數進行代碼走查不就搞定了嗎?

光是走查(沒有對應的單元測試用例),當代碼變更後,需要重新走查,之前的走查成果無法繼承。

隨著單元模塊邏輯複雜度的提升,必須編寫測試代碼進行代碼質量的保障(大神除外)。

對於擁有單元測試用例的模塊,當此單元進行較大的代碼優化後,可以通過已有的單元測試用例快速評估優化後的代碼質量、及時發現代碼錯誤。

為什麼要使用測試框架,直接單元測試不香嗎?先說結論:不香,直接編寫測試用例進行單元測試整體效率要低得多。

測試框架是對整個測試系統的可重複使用設計,可重複意味著自動化。有了測試框架,測試人員不再需要編寫瑣碎的測試代碼,從而可以專注於測試用例本身,使得測試更聚焦。

好的單元測試框架及Google Test的優勢(摘自GoogleTest Primer.md,你理解為GTest自己吹捧自己也沒錯)。

GTest使每個測試用例運行在不同的對象中從而使測試隔離。

當一個測試失敗時,GTest允許你將它運行在隔離的環境下從而達到快速調試的目的。

測試有良好的組織,可以反映被測試代碼的結構。GTest將相關測試劃分到一個測試組內,組內的測試能共享數據,使測試易於維護。

與平臺無關的代碼,其測試代碼也應該和平臺無關,GTest能在不同的OS下工作,並且支持不同的編譯器。

當用例執行失敗時,可以提供儘可能多的有效信息,以便定位問題。

GTest可以定製出錯時的有效信息,使得更容易定位故障。

GTest能在測試用例之間復用測試資源,使單元測試更高效。

囉嗦了這麼久,下面正式開始。

注意:所有示例均已在Windows10+Visual Studio 2019上調試通過,函數到接口的單元測試示例為GoogleTest的附帶示例,作者進行了標註並結合實際情況進行了少量改動。

Windows10 + Visual Studio 2019(vs2019已集成GTest)。

1.打開VS 2019,創建新項目。

2.搜索關鍵字 Google,可以得到 Google Test的項目模板:Write C++ unit tests using Google Test。

3.選擇項目模板Google Test,點擊下一步。

4.配置項目名稱和位置,點擊創建。

5.測試項目配置如下:

6.等待VS創建好新項目:如圖所示,創建好了名為myGTest的項目。

1.在test.cpp中編寫函數Factorial() ,並編寫Factorial( ) 對應的單元測試用例(Factorial( )函數用於計算整數n的階乘)。

2. 觀察測試用例TEST(TestFactorialFunc, FirstGTest) 。

(1) 編寫測試用例使用了TEST宏,它有兩個參數:TEST[TestCaseName,TestName],TestCaseName對應測試用例集名稱,TestName是歸屬的測試用例名稱。

(2)對檢查點的檢查,使用了EXPECT_EQ宏,用來比較兩個數字是否相等。

可以看到,通過GTest編寫用例還是蠻方便的。

Google打包了一系列EXPECT_* 和 ASSERT_* 宏,EXPECT和ASSERT的區別:

1. 在VS2019中創建新項目 Practice(待測項目)。

其中的Practice.cpp包含main( )函數:

#include <iostream>  using namespace std;    int main()  {      cout << "Start Google Test…\n";      return 0;}

(左右滑動查看完整代碼)

2. 創建Practice項目配套的gtest項目。

(1)文件-新建-項目,打開「創建新項目」窗口。

(2)選擇創建Google Test,然後點擊下一步。

(3)配置新項目 窗口:項目名稱自定,解決方案選擇是添加到解決方案。然後點擊創建:

(5)如圖所示,已經創建好了Practice項目 對應的gtest項目practice_gtest。

1.設置項目之間的依賴,可以簡化頭文件的路徑描述:

2.設置依賴關係

(1) 右鍵點擊Practice_gtest,選擇屬性。

(2) 在屬性頁中,依次定位到:配置屬性 --- C/C++ --- 常規 --- 附加包含目錄,點擊下拉框後選擇編輯。

(3) 在彈出的附加包含目錄窗口中設定依賴關係:輸入待測項目的目錄地址。

1.在practice項目中新建文件 Calc.h、Calc.cpp,用於模擬加減乘除運算。

class Calc  {  public:      int  Add(int a, int b);      int  Minus(int a, int b);      int  Multi(int a, int b);      float Divide(float a, float b);  };  

(左右滑動查看完整代碼)

#include "Calc.h"    int Calc::Add(int a, int b)      return a + b;    int Calc::Minus(int a, int b)      return a - b;    int Calc::Multi(int a, int b)      return a * b;    float Calc::Divide(float a, float b)      return a / b;

(左右滑動查看完整代碼)

2.在gtest測試工程中新建ClassCalcTest.cpp,用於測試類Calc。

這裡使用的還是TEST宏,測試用例分別對應代碼中的加減乘除。

一個問題:為什麼測試對象是 Calc.h,而不是Calc.cpp?

因為調用的是接口(*.h頭文件),而不是實現(*.cpp文件):

#include "pch.h"  #include "Calc.h"    Calc calculation;    TEST(CalcClassTest, add)  {      EXPECT_EQ(3,calculation.Add(1, 2));  }    TEST(CalcClassTest, minus)  {      EXPECT_EQ(calculation.Minus(1, 2), -1);  }    TEST(CalcClassTest, multi)  {      EXPECT_EQ(calculation.Multi(1, 2), 2);  }    TEST(CalcClassTest, devide)  {      EXPECT_FLOAT_EQ(calculation.Divide(1, 2),0.5);  } 

(左右滑動查看完整代碼)

3.正式啟動測試前,需要先設定好對應目標文件的地址。

(1) 進入測試工程Practice_gtest的屬性設定界面(右鍵點擊項目,在彈出菜單中選擇屬性)。

(2) 依次定位到連結器-輸入-附加依賴項,點擊下拉框,進行編輯。

(3) 輸入類Calc的目標文件 Calc.obj的地址。

注意:當測試多個類時,需要分別添加對應的obj文件,該場景下不能使用通配符 * ,否則執行測試時會報錯。

代碼文件是 sample01.h、sample01.cpp,對應的單元測試文件是sample01UnitTest.cpp。

sample01.h進行了函數聲明:

int Factorial(int n);    bool IsPrime(int n);

(左右滑動查看完整代碼)

sample01.cpp中撰寫了待測試的函數(對應的接口文件是sample01.h):Factorial( )函數用於求一個數的階乘,IsPrime( )函數用於判定一個數是否是素數:

#include "sample01.h"    int Factorial(int n)  {      int result = 1;        for (int i = 1; i <= n; i++)          result *= i;        return result;  }    bool IsPrime(int n)   {      if (n <= 1) return false;         if (n % 2 == 0) return n == 2;          for (int i = 3; ; i += 2)      {          if (i > n / i) break;          if (n % i == 0) return false;      }        return true;  }

(左右滑動查看完整代碼)

注意:編寫單元測試用例時,你的重點並不是讀懂每一行原碼,而是弄清楚這個單元對應的主體功能、調用方式 以及 代碼的邏輯構成。

你的目標是:編寫的單元測試用例可以覆蓋所有的代碼邏輯分支。

在單元測試用例sample01UnitTest.cpp中,可以看到他覆蓋了待測函數的各個分支(請參考下圖)。

大家可以通過閱讀下面的源碼和注釋加深理解:

  TEST(FactorialTest, Negative) {      EXPECT_EQ(1, Factorial(-5));      EXPECT_EQ(1, Factorial(-1));      EXPECT_GT(Factorial(-10), 0);  }    TEST(FactorialTest, Zero)   {      EXPECT_EQ(1, Factorial(0));  }    TEST(FactorialTest, Positive)   {      EXPECT_EQ(1, Factorial(1));      EXPECT_EQ(2, Factorial(2));      EXPECT_EQ(6, Factorial(3));      EXPECT_EQ(40320, Factorial(8));  }        TEST(IsPrimeTest, Negative)   {      EXPECT_FALSE(IsPrime(-1));      EXPECT_FALSE(IsPrime(-2));      EXPECT_FALSE(IsPrime(INT_MIN));  }    TEST(IsPrimeTest, Trivial)   {      EXPECT_FALSE(IsPrime(0));      EXPECT_FALSE(IsPrime(1));      EXPECT_TRUE(IsPrime(2));      EXPECT_TRUE(IsPrime(3));  }    TEST(IsPrimeTest, Positive)   {      EXPECT_FALSE(IsPrime(4));      EXPECT_TRUE(IsPrime(5));      EXPECT_FALSE(IsPrime(6));      EXPECT_TRUE(IsPrime(23));  }   

(左右滑動查看完整代碼)

類測試、接口測試以及Test Fixture的使用將在下面幾篇介紹~

相關焦點

  • 玩轉Google開源C++單元測試框架Google Test系列(gtest)之六 - 運行參數
    id=13700018一、前言 什仫是gtestgtest是一個跨平臺的(Liunx、Mac OS X、Windows 、Cygwin 、Windows CE and Symbian ) C++單元測試框架,由google公司發布。
  • Google Test框架
    前言使用 C/C++ 開發的項目,在進行測試的時候,要麼自己寫一些簡單的例子進行測試,要麼使用第三方框架。
  • JUnit單元測試框架的使用
    關注公眾號,後臺回復「接口測試」我們寫單元測試,一般都會用到一個或多個單元測試框架,在這裡,我們介紹一下JUnit4這個測試框架。這是Java界用的最廣泛,也是最基礎的一個框架,其他的很多框架,包括我們後面會看到的Robolectric,都是基於或兼容JUnit4的。然而首先要解決的問題是。。。為什麼要使用單元測試框架呢或者換句話說,單元測試框架能夠為我們做什麼呢?從最基本的開始說起,假如我們有這樣一個類:
  • 玩轉Google開源C++單元測試框架Google Test系列(gtest)之一 - 初識gtest
    二、下載如果不記得網址, 直接在google裡搜gtest,第一個就是。目前gtest的最新版本為1.3.0,從下列地址可以下載到該最新版本:http://googletest.googlecode.com/files/gtest-1.3.0.zip http://googletest.googlecode.com/files/gtest-1.3.0.tar.gz http://googletest.googlecode.com
  • linux下使用gtest框架進行c/c++測試-gtest環境搭建
    前言google test(以下簡稱gtest)是谷歌的開源C++單元測試框架,用來做c/c++的單元測試比較方便。下面對於它在linux下的使用環境搭建做簡單介紹。準備gtest框架在github網站下載gtest框架https://github.com/google/googletest解壓後,進入googletest目錄。
  • Android單元測試的簡單使用
    網上的單元測試教程都特別深奧或者千篇一律,像我這種小白看完之後也是摸不著頭腦,也不知道該如何下手,所以我就寫了這篇簡單的Android單元測試教程,也算是拋磚引玉。hl=zh-cn#test-types  測試前的準備  首先要在 moudle-name下的build.gradle文件裡面引入相關的依賴:  (只貼出了和測試相關的)  其中Espresso是Google官方提供並推薦使用的測試庫。
  • 萬字詳文:徹底搞懂 Jest 單元測試框架
    徹底搞懂 Jest 單元測試框架本文主要給大家深入了解 Jest 背後的運行原理,並從零開始簡單實現一個 Jest 單元測試的框架,方便了解單元測試引擎是如何工作的,Jest 編寫單測相信我們已經很熟悉了,但 Jest 是如何工作的我們可能還很陌生,那讓我們一起走進 Jest 內心,一同探究單元測試引擎是如何工作的。
  • PHP單元測試利器:PHPUNIT初探
    人們往往會說:既然單元測試這麼好,為什麼那麼多人還是不大願意去寫單元測試呢?有以下幾種理解上的誤曲:  1、認為編寫單元測試太浪費時間。雖然目前很多IDE工具都為編寫單元測試建立好了框架,但還是要開發者編寫一些單元測試的代碼的。就象很多開發中的最佳實踐一樣,用正確的方法去做正確的事情會為開發節省大量的時間。
  • 使用Kubernetes客戶端庫進行單元測試
    使用 Kubernetes 客戶端庫可以通過模擬集群來測試代碼。作為在構建 kubernetes / minikube 時,第一批使用kubernetes / client-go 庫的用戶之一,我曾經詳盡的設計了服務、調度單元和部署來對我的代碼進行單元測試。現在,我發現有一種更簡單的方法可以用更少的代碼行來做同樣的事情。
  • 初探GoogleTest
    Google Test(GTest)是谷歌開源的一套針對C++語言、跨平臺的測試框架,與其他框架相比,其優勢在於:1、每個測試用例都對應一個唯一的對象
  • Android 單元測試只看這一篇就夠了
    單元測試的分類本地測試(Local tests): 只在本地機器JVM上運行,以最小化執行時間,這種單元測試不依賴於Android框架,或者即使有依賴,也很方便使用模擬框架來模擬依賴,以達到隔離Android依賴的目的,模擬框架如google推薦的[Mockito][1];儀器化測試(Instrumented tests):
  • Google官方的UI自動化測試框架—— Espresso
    本篇文章來自 新根 的投稿,是關於Espresso自動化UI測試框架的基礎講解以及案例分析,旨在幫助那些還未入門的朋友了解相關知識
  • Android單元測試研究與實踐
    Java單元測試中,良好的單元測試是需要保證所有函數執行正確的,即所有邊界條件都驗證過,一個用例只測一個函數,便於維護。在Android單元測試中,並不要求對所有函數都覆蓋到,像Android SDK中的函數回調則不用測試。Android單元測試在Android中,單元測試的本質依舊是驗證函數的功能,測試框架也是JUnit。
  • 使用MockMVC進行Controller單元測試
    引入由於MockMVC是Spring框架自帶的測試組件,因此只要在項目中引入spring-boot-starter-test
  • 8個超實用的Java測試工具和框架
    Arquillian可以用來管理單個或多個容器的生命周期,綑紮測試用例,從屬類和資源。它還能夠部署歸檔到容器中,在容器中執行測試、捕獲結果,並創建報告。Arquillian集成了常見的測試框架,如JUnit 4、TestNG 5,並允許使用現有的IDE發布測試,並且由於其模塊化的設計使得能夠運行Ant和Maven測試插件。
  • 【轉】在Android Studio中進行單元測試和UI測試
    目錄1.概述2.創建新的Android Studio工程3.配置支持單元測試的工程4.創建第一個單元測試5.運行單元測試6.配置支持Instrumentation測試的工程7.為app添加簡單的交互8.創建並運行Espresso測試
  • Java單元測試之JUnit 5快速上手
    無論是對自己的編碼能力的提高,還是項目質量的提升,都是大有好處,本文將介紹 Java 單元測試框架 JUnit 5 的基礎認識和使用來編寫單元測試,希望同樣對你有所幫助。JUnit,它與另一個框架 TestNG 佔據了 Java領域裡單元測試框架的主要市場,其中 JUnit 有著較長的發展歷史和不斷演進的豐富功能,備受大多數 Java 開發者的青睞。
  • Python測試框架之unittest和pytest
    近期打算重新優化框架,著重解決運行效率低的問題。最近調研了一下另一種主流測試框架Pytest,Pytest是一個非常成熟的全功能的Python測試框架,本文主要對比了Unittest和Pytest這兩種較為流行的Python測試框架。
  • 蘑菇街支付金融Android單元測試實踐
    對單元測試和TDD情有獨鍾。大家好,我是蘑菇街支付金融部門的鄒勇,花名叫小創。今天很高興跟大家分享一下安卓的單元測試在蘑菇街支付金融的實踐。下面,我們從為什麼開始。首先要介紹為什麼蘑菇街支付金融這邊會採用單元測試的實踐。說起來比較巧,剛開始的時候,只是我一個人會寫單元測試。後來老闆們知道了,覺得這是件很有價值的事情,於是就叫我負責我們組的單元測試這件事情。
  • Pytest 測試框架入門
    本文作者:劉春明原文連結:http://blog.csdn.net/liuchunming033/1、 Pytest 介紹pytest 是 python 的一種單元測試框架,與 python 自帶的 unittest 測試框架類似,但是比 unittest 框架使用起來更簡潔,效率更高。