国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

手把手教你使用gtest寫單元測試

這篇具有很好參考價值的文章主要介紹了手把手教你使用gtest寫單元測試。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

開源框架:gtest,它主要用于寫單元測試,檢查真自己的程序是否符合預(yù)期行為。這不是QA(測試工程師)才學的,也是每個優(yōu)秀后端開發(fā)codoer的必備技能。

本期博文內(nèi)容及使用的demo,參考:

  • Googletest Basic Guide[1]

  • Googletest Samples [2]

構(gòu)建依賴環(huán)境

按照慣例,先介紹下怎么基于CMakeLists.txt構(gòu)建依賴環(huán)境。

由于Google沒有為googletest/samples中的samples寫CMakeLists.txt,因此,gtest從github克隆下來后,也無法直接運行這些samples。

為方便大家跟著本文一起實踐,獲得更好的學習體驗,在后臺回復「gtest」即可獲取我配置好的gtest壓縮包。

當前目錄結(jié)構(gòu)如下:

$ tree -L 2
demo
├── CMakeLists.txt  
├── build        # 空的文件夾
├── include
│   ├── CMakeLists.txt
│   ├── gflags
│   └── googletest
├── main.cc
└── my_gtest_demo_1.cc

然后,在demo/build路徑下,執(zhí)行命令:

$ cmake .. && make -j 4

這些samples生成的可執(zhí)行文件都在demo/build/bin路徑下。

這樣,就介紹完前提準備,下面開始進入正題。

assertion

在gtest中,是通過斷言(assertion)來判斷代碼實現(xiàn)的功能是否符合預(yù)期。斷言的結(jié)果分為success、non-fatal failture和fatal failture。

根據(jù)斷言失敗的種類,gtest提供了兩種斷言函數(shù):

  • success:即斷言成功,程序的行為符合預(yù)期,程序繼續(xù)向下允許。

  • non-fatal failure:即斷言失敗,但是程序沒有直接crash,而是繼續(xù)向下運行。 gtest提供了宏函數(shù)EXPECT_XXX(expected, actual):如果condition(expected, actual)返回false,則EXPECT_XXX產(chǎn)生的就是non-fatal failure錯誤,并顯示相關(guān)錯誤。

  • fatal failure:斷言失敗,程序直接crash,后續(xù)的測試案例不會被運行。 gtest提供了宏函數(shù)ASSERT_XXX(expected, actual)。 在寫單元測試時,更加傾向于使用EXPECT_XXX,因為ASSERT_XXX是直接crash退出的,可能會導致一些內(nèi)存、文件資源沒有釋放,因此可能會引入一些bug。

具體的EXPECT_XXX、ASSERT_XXX函數(shù)及其判斷條件,如下兩個表。

表1 一元比較

ASSERT

EXPECT

Verifies

ASSERT_TRUE(condition);

EXPECT_TRUE(condition);

condition is true

ASSERT_FALSE(condition)

EXPECT_FALSE(condition)

condition is false

表2 二元比較

ASSERT

EXPECT

Condition

ASSERT_EQ(val1, val2);

EXPECT_EQ(val1, val2);

val1 == val2

ASSERT_NE(val1, val2);

EXPECT_NE(val1, val2);

val1 != val2

ASSERT_LT(val1, val2);

EXPECT_LT(val1, val2);

val1 < val2

ASSERT_LE(val1, val2);

EXPECT_LE(val1, val2);

val1 <= val2

ASSERT_GT(val1, val2);

EXPECT_GT(val1, val2);

val1 > val2

ASSERT_GE(val1, val2);

EXPECT_GE(val1, val2);

val1 >= val2

Quick Start

下面以EXPECT_XXX為例子,快速開始使用gtest吧。

對于EXPECT_XXX,無論條件是否滿足,都會繼續(xù)向下運行,但是如果條件不滿足,在報錯的地方會顯示:

  1. 沒有通過的那個EXPECT_XXX函數(shù)位置;

  2. EXPECT_XXX第一個參數(shù)的值,即期待值

  3. EXPECT_XXX第二個參數(shù)的值,即實際值

如下demo:

// in gtest_demo_1.cc
#include <gtest/gtest.h>

int add(int lhs, int rhs) { return lhs + rhs; }

int main(int argc, char const *argv[]) {

    EXPECT_EQ(add(1,1), 2); // PASS
    EXPECT_EQ(add(1,1), 1) << "FAILED: EXPECT: 2, but given 1";; // FAILDED
    
    return 0;
}

編譯執(zhí)行后輸出如下:

$ ./gtest_demo_1
/Users/self_study/Cpp/OpenSource/demo/gtest_demo_1.cc:9: Failure
Expected equality of these values:
  add(1,1)
    Which is: 2                # 期待的值
  1                            # 給定的值
FAILED: EXPECT: 2, but given 1 # 自己添加的提示信息 

可能你注意到了,在EXPECT_EQ(add(1,1), 1)后有個<<,這是因為gtest允許添加自定義的描述信息,當這個語句測試未通過時就會顯示,比如上面的"FAILED: EXPECT: 2, but given 1"。

這個<<和std::ostream接受的類型一致,即可以接受std::ostream可以接受的類型。

相關(guān)視頻推薦

程序員精進之路-從googletest測試框架開始

c++后端必學:googletest中的設(shè)計模式

c/c++后端開發(fā)需要學些什么?迭代13次的c/c++后端開發(fā)學習路線分享

需要C/C++ Linux服務(wù)器架構(gòu)師學習資料加qun579733396獲取(資料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享

gtest,單元測試,c++,googletest,后端開發(fā)

?

TEST

下面以googletest/samples中的sample1_unittest.cc中的demo為例,介紹如何更好地組織測試案例。

一個簡單計算階乘函數(shù)Factorial實現(xiàn)如下:

int Factorial(int n) {
  int result = 1;
  for (int i = 1; i <= n; i++) {
    result *= i;
  }

  return result;
}

怎么使用gtest來測試這個函數(shù)的行為?

按照上面的quick start可知,這個時候就可以使用EXPECT_EQ宏來判斷:

 EXPECT_EQ(1, Factorial(-5)); // 測試計算負數(shù)的階乘
  EXPECT_EQ(1, Factorial(0));   // 測試計算0的階乘
  EXPECT_EQ(6, Factorial(3));   // 測試計算正數(shù)的階乘 

但是當測試案例規(guī)模變大,不好組織。

因此,為了更好的組織test cases,比如針對Factorial函數(shù),輸入是負數(shù)的cases為一組,輸入是0的case為一組,正數(shù)cases為一組。gtest提供了一個宏TEST(TestSuiteName, TestName),用于組織不同場景的cases,這個功能在gtest中稱為test suite。

用法如下:

// 下面三個 TEST 都是屬于同一個 test suite,即 FactorialTest

問題來了,怎么運行這些TEST?

在sample1_unittest.cc的main函數(shù)中,添加RUN_ALL_TESTS函數(shù)即可。

int main(int argc, char **argv) {
  printf("Running main() from %s\n", __FILE__);
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS(); 
}

在build/bin路徑下,執(zhí)行對應(yīng)的可執(zhí)行文件,輸出如下:

$./sample1_unittest 
Running main() from /Users/self_study/Cpp/OpenSource/demo/include/googletest/googletest/samples/sample1_unittest.cc
[==========] Running 6 tests from 2 test suites. # 在 sample1_unittest.cc 中有兩個 test suites
[----------] Global test environment set-up.    

# 第一個 test suite,即上面的 FactorialTest
[----------] 3 tests from FactorialTest     # 3 組
[ RUN      ] FactorialTest.Negative         # Negative 組輸出
[       OK ] FactorialTest.Negative (0 ms)  # OK 表示 Negative 組全部測試通過
[ RUN      ] FactorialTest.Zero             # Zero組輸出 
[       OK ] FactorialTest.Zero (0 ms)    
[ RUN      ] FactorialTest.Positive         # Positive組輸出
[       OK ] FactorialTest.Positive (0 ms)   
[----------] 3 tests from FactorialTest (0 ms total)

# sample1_unitest 另一個測試案例的輸出 ...

[----------] Global test environment tear-down  
[==========] 6 tests from 2 test suites ran. (0 ms total) 
[  PASSED  ] 6 tests.              # 全部測試結(jié)果:PASS表示全部通過 

下面稍微修改下sample1_unittest.cc中的代碼,來產(chǎn)生一個錯誤:

TEST(FactorialTest, Negative) {
  EXPECT_EQ(10, Factorial(-5));  // 正確的應(yīng)該是  EXPECT_EQ(1, Factorial(-5));
  // ...
}

重新編譯,運行結(jié)果如下:

$ ./sample1_unittest 
Running main() from /Users/self_study/Cpp/OpenSource/demo/include/googletest/googletest/samples/sample1_unittest.cc
[==========] Running 6 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 3 tests from FactorialTest
[ RUN      ] FactorialTest.Negative          # 開始運行上面修改的那個組
/Users/self_study/Cpp/OpenSource/demo/include/googletest/googletest/samples/sample1_unittest.cc:79: Failure                 # 測試失敗,并指出錯誤case的位置
Expected equality of these values:           # 期待的值
  10
  Factorial(-5)                              # 實際計算出的值
    Which is: 1
[  FAILED  ] FactorialTest.Negative (0 ms)   # 這組case測試狀態(tài):FAILED
[ RUN      ] FactorialTest.Zero              # 下面繼續(xù)運行
[       OK ] FactorialTest.Zero (0 ms)
[ RUN      ] FactorialTest.Positive
[       OK ] FactorialTest.Positive (0 ms)
[----------] 3 tests from FactorialTest (0 ms total)

# ...

[----------] Global test environment tear-down
[==========] 6 tests from 2 test suites ran. (0 ms total)
[  PASSED  ] 5 tests.          
[  FAILED  ] 1 test, listed below:     # 1個test失敗
[  FAILED  ] FactorialTest.Negative    # 失敗的test suite及其組

 1 FAILED TEST

此外,在TEST宏函數(shù)中,也可以像個普通函數(shù)一樣,定義變量之類的行為。

比如在sample2_unittest.cc中,測試一個自定義類MyString的復制構(gòu)造函數(shù)是否表現(xiàn)正常:

const char kHelloString[] = "Hello, world!";

// 在 TEST內(nèi)部,定義變量
TEST(MyString, CopyConstructor) {
  const MyString s1(kHelloString);
  const MyString s2 = s1;
  EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString));
}

為獲得進一步學習,讀者可以自行調(diào)整sample1_unittest.cc、sample2_unittest.cc中的TEST行為,加深對gtest的TEST宏的理解。

TEST_F

下面介紹gtest中更為高級的功能:test fixture,對應(yīng)的宏函數(shù)是TEST_F(TestFixtureName, TestName)。

fixture,其語義是固定的設(shè)施,而test fixture在gtest中的作用就是為每個TEST都執(zhí)行一些同樣的操作。

比如,要測試一個隊列Queue的各個接口功能是否正常,因此就需要向隊列中添加元素。如果使用一個TEST函數(shù)測試Queue的一個接口,那么每次執(zhí)行TEST時,都需要在TEST宏函數(shù)中定義一個Queue對象,并向該對象中添加元素,就很冗余、繁瑣。

怎么避免這部分冗余的過程?

TEST_F就是完成這樣的事情,它的第一個參數(shù)TestFixtureName是個類,需要繼承testing::Test,同時根據(jù)需要實現(xiàn)以下兩個虛函數(shù):

  • virtual void SetUp():在TEST_F中測試案例之前運行;

  • virtual void TearDown():在TEST_F之后運行。

可以類比對象的構(gòu)造函數(shù)和析構(gòu)函數(shù)。這樣,同一個TestFixtureName下的每個TEST_F都會先執(zhí)行SetUp,最后執(zhí)行TearDwom。

此外,testing::Test還提供了兩個static函數(shù):

  • static void SetUpTestSuite():在第一個TEST之前運行

  • static void TearDownTestSuite():在最后一個TEST之后運行

以sample3-inl中實現(xiàn)的class Queue為例:

class QueueTestSmpl3 : public testing::Test { // 繼承了 testing::Test
protected:  
  
  static void SetUpTestSuite() {
    std::cout<<"run before first case..."<<std::endl;
  } 

  static void TearDownTestSuite() {
    std::cout<<"run after last case..."<<std::endl;
  }
  
  virtual void SetUp() override {
    std::cout<<"enter into SetUp()" <<std::endl;
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  virtual void TearDown() override {
    std::cout<<"exit from TearDown" <<std::endl;
  }
  
  static int Double(int n) {
    return 2*n;
  }
  
  void MapTester(const Queue<int> * q) {
    const Queue<int> * const new_q = q->Map(Double);

    ASSERT_EQ(q->Size(), new_q->Size());

    for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();
         n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {
      EXPECT_EQ(2 * n1->element(), n2->element());
    }

    delete new_q;
  }

  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};

下面是sample3_unittest.cc中的TEST_F:

// in sample3_unittest.cc

// Tests the default c'tor.
TEST_F(QueueTestSmpl3, DefaultConstructor) {
  // !!! 在 TEST_F 中可以使用 QueueTestSmpl3 的成員變量、成員函數(shù) 
  EXPECT_EQ(0u, q0_.Size());
}

// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {
  int * n = q0_.Dequeue();
  EXPECT_TRUE(n == nullptr);

  n = q1_.Dequeue();
  ASSERT_TRUE(n != nullptr);
  EXPECT_EQ(1, *n);
  EXPECT_EQ(0u, q1_.Size());
  delete n;

  n = q2_.Dequeue();
  ASSERT_TRUE(n != nullptr);
  EXPECT_EQ(2, *n);
  EXPECT_EQ(1u, q2_.Size());
  delete n;
}

// Tests the Queue::Map() function.
TEST_F(QueueTestSmpl3, Map) {
  MapTester(&q0_);
  MapTester(&q1_);
  MapTester(&q2_);
}

以TEST_F(QueueTestSmpl3, DefaultConstructor)為例,再具體講解下TEST_F的運行流程:

  1. gtest構(gòu)造一個QueueTestSmpl3對象t1;

  2. t1.setUp初始化t1

  3. 第一個TEST_F即DefaultConstructor開始運行并結(jié)束

  4. t1.TearDwon運行,用于清理工作

  5. t1被析構(gòu)

因此,sample3_unittest.cc輸出如下:

% ./sample3_unittest
Running main() from /Users/self_study/Cpp/OpenSource/demo/include/googletest/googletest/samples/sample3_unittest.cc
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 3 tests from QueueTestSmpl3
run before first case...    # 所有的test case 之前運行
[ RUN      ] QueueTestSmpl3.DefaultConstructor
enter into SetUp()          # 每次都會運行
exit from TearDown
[       OK ] QueueTestSmpl3.DefaultConstructor (0 ms)
[ RUN      ] QueueTestSmpl3.Dequeue
enter into SetUp()          # 每次都會運行
exit from TearDown
[       OK ] QueueTestSmpl3.Dequeue (0 ms)
[ RUN      ] QueueTestSmpl3.Map
enter into SetUp()          # 每次都會運行
exit from TearDown
[       OK ] QueueTestSmpl3.Map (0 ms)
run after last case...      # 所有test case結(jié)束之后運行
[----------] 3 tests from QueueTestSmpl3 (0 ms total)

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

TEST_F相比較TEST可以更加簡潔地實現(xiàn)功能測試。

?文章來源地址http://www.zghlxwxcb.cn/news/detail-565136.html

到了這里,關(guān)于手把手教你使用gtest寫單元測試的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • C++單元測試GoogleTest和GoogleMock十分鐘快速上手(gtest&gmock)

    下載 安裝 重要文件 googletest gtest/gtest.h libgtest.a libgtest_main.a 當不想寫 main 函數(shù)的時候,可以直接引入 libgtest_main.a; 否則 googlemock gmock/gmock.h libgmock.a libgmock_main.a 一 .斷言 gtest 中的斷言分成兩大類: ASSERT_* 系列:如果檢測失敗就直接退出 當前函數(shù) EXPECT_* 系列:如果檢測失敗

    2024年02月06日
    瀏覽(22)
  • 手把手教你如何快速定位bug,如何編寫測試用例,快來觀摩......

    手把手教你如何快速定位bug,如何編寫測試用例,快來觀摩......

    手把手教你如何快速定位bug,如何編寫測試用例,快來觀摩......手把手教你如何快速定位bug,如何編寫測試用例,快來觀摩......作為一名測試人員如果連常見的系統(tǒng)問題都不知道如何分析,頻繁將前端人員問題指派給后端人員,后端人員問題指派給前端人員,那么在團隊里你在開發(fā)

    2024年01月20日
    瀏覽(23)
  • 手把手教你如何使用SimiliarWeb

    手把手教你如何使用SimiliarWeb

    在之前的“手把手教你如何使用Google Trends”文章中我們講到從事跨境電商的賣家第一步遇到的問題是“客戶在哪里?”該如何推廣我的產(chǎn)品?因此若想自己的店鋪做大做好,則需要工具來幫助分析市場行情,根據(jù)市場行情調(diào)整自己的業(yè)務(wù)狀況。小編在上篇中已經(jīng)講解了三個特

    2024年02月09日
    瀏覽(103)
  • 手把手教你如何使用Docker

    手把手教你如何使用Docker

    我們在公司開發(fā)中,會有開發(fā)環(huán)境,測試環(huán)境,上線環(huán)境, 比如我們開發(fā)人員開發(fā)好了一個項目,在開發(fā)環(huán)境中運行正常,但測試人員拉到測試環(huán)境就跑不起來【jdk版本等】,或者上線的時候運行不起來,這時候就要為每個機器配置一個環(huán)境,那運維人員不得累死?【哈哈,

    2024年02月10日
    瀏覽(104)
  • 手把手教你使用gdb調(diào)試器

    手把手教你使用gdb調(diào)試器

    所謂調(diào)試,指的是對編好的程序用各種手段進進行查錯和排非錯的過程。進行這種查錯處理時,下面將講解如何使用gdb進行程序的調(diào)試。? gdb?簡介 gdb是一個功能強大的調(diào)試工具,可以用來調(diào)試C程序或C++程序。在使用這個工具進行程序調(diào)試時,主要涉及下面四個方面的操作。

    2024年02月16日
    瀏覽(31)
  • 怎么用AI繪畫?手把手教你使用

    怎么用AI繪畫?手把手教你使用

    與傳統(tǒng)的繪畫方式不同,AI繪畫軟件采用了人工智能算法和計算機視覺技術(shù),使藝術(shù)作品的創(chuàng)作變得更加智能化和自動化。這樣,即使一個看不懂顏料,也毫無繪畫經(jīng)驗的業(yè)余者也能創(chuàng)作出可圈可點的藝術(shù)品了。AI繪畫軟件因此被越來越多的創(chuàng)作者和愛好者所使用。那你們知道

    2024年02月15日
    瀏覽(97)
  • 手把手教你 iconfont 導入使用及相關(guān)配置

    手把手教你 iconfont 導入使用及相關(guān)配置

    iconfont是阿里旗下的一套圖標庫,UI設(shè)計師設(shè)計號圖標后,會將圖標上傳到iconfont的項目庫中。前端開發(fā)人員需要下載項目圖標,并在項目中使用。 iconfont相對于傳統(tǒng)的直接導入圖標進入頁面,有以下幾點優(yōu)勢: 體積更小,頁面加載速度更快 解決圖片像素點會隨頁面變化而模

    2024年02月07日
    瀏覽(28)
  • 【碼農(nóng)教程】手把手教你Mockito的使用

    【碼農(nóng)教程】手把手教你Mockito的使用

    1)Mockito:簡單輕量級的做mocking測試的框架; 2)mock對象:在調(diào)試期間用來作為真實對象的替代品; 3)mock測試:在測試過程中,對那些不容易構(gòu)建的對象用一個虛擬對象來代替測試的方法就叫mock測試; 4)stub:打樁,就是為mock對象的方法指定返回值(可拋出異常); 5)

    2024年02月05日
    瀏覽(29)
  • 手把手教你使用Segformer訓練自己的數(shù)據(jù)

    手把手教你使用Segformer訓練自己的數(shù)據(jù)

    使用Transformer進行語義分割的簡單高效設(shè)計。 將 Transformer 與輕量級多層感知 (MLP) 解碼器相結(jié)合,表現(xiàn)SOTA!性能優(yōu)于SETR、Auto-Deeplab和OCRNet等網(wǎng)絡(luò) 相比于ViT,Swin Transfomer計算復雜度大幅度降低,具有輸入圖像大小線性計算復雜度。Swin Transformer隨著深度加深,逐漸合并圖像塊來

    2024年01月20日
    瀏覽(119)
  • 手把手教你使用Markdown:從入門到精通

    手把手教你使用Markdown:從入門到精通

    本篇文章由卷不動的小白撰寫,為讀者提供了一份詳盡的Markdown語法指南。

    2024年02月03日
    瀏覽(110)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包