白盒單元測試框架

      網(wǎng)友投稿 898 2022-05-30

      1??????引言

      當你開始開發(fā)新的庫/類/程序時,你需要做的第一件事是什么?

      沒錯。你需要從單元測試模塊開始。

      現(xiàn)在我們來看看C++?白盒測試比較流行的單元測試框架。

      2??????Boost.Test

      Boost.Test是一個C++03/11/14/17單元測試庫,可用于多種平臺和編譯器。

      該庫是Boost的一部分。該庫的最新版本可以從boost網(wǎng)站上獲得。

      該庫的完整使用說明可以從http://www.boost.org/doc/libs/release/libs/test/。

      2.1????官方網(wǎng)站

      http://www.boost.org

      https://github.com/boostorg/test

      2.2????許可

      Boost Software License - Version 1.0

      2.3????主要特點及使用方法

      l??容易上手:

      n??下載并使用boost庫

      n??用下面兩行創(chuàng)建一個測試模塊:

      #define?BOOST_TEST_MODULE?your_test_module

      #include?

      n??編寫一個測試案例:

      BOOST_AUTO_TEST_CASE(?your_test_case?)?{

      std::vector?a{1,?2};

      std::vector?b{1,?2};

      BOOST_TEST(?a?==?b?);

      }

      n??編譯運行

      l??強大而獨特的測試斷言宏BOOST_TEST,可用來檢測浮點數(shù)、集合、字符串......并支持適當?shù)谋容^范式。

      l??自注冊測試用例,在測試套件中組織用例,在測試用例、套件或全局上應用Fixture。

      l??為故障的高級診斷提供斷言。

      l??強大且可擴展的數(shù)據(jù)集測試。

      l??可為測試用例和套件添加高級描述、組/標簽和依賴關系的裝飾。

      l??強大的命令行選項和測試案例過濾器。

      l??為第三方工具提供可擴展的日志記錄、XML和JUNIT輸出(例如:連續(xù)集成)。

      l??各種方法(如共享/靜態(tài)庫/頭)用以加快集成和/或編譯/構建周期,生成更小的二進制文件。

      2.4????用法

      2.4.1????簡單測試

      看一下簡單的測試用例。

      #include?

      #define?BOOST_TEST_MODULE?MyTest

      #include?

      BOOST_AUTO_TEST_CASE(?my_test?)

      {

      my_class?test_object(?"qwerty"?);

      BOOST_CHECK(?test_object.is_valid()?);

      }

      #define?BOOST_TEST_MODULE?MyTest

      #include?

      int?add(?int?i,?int?j?)?{?return?i+j;?}

      BOOST_AUTO_TEST_CASE(?my_test?)

      {

      //?七種方法來檢測和報告相同的錯誤:

      BOOST_CHECK(?add(?2,2?)?==?4?);????????//?#1?出錯后繼續(xù)

      BOOST_REQUIRE(?add(?2,2?)?==?4?);??????//?#2?出錯后拋出

      if(?add(?2,2?)?!=?4?)

      BOOST_ERROR(?"Ouch..."?);????????????//?#3?出錯后繼續(xù)

      if(?add(?2,2?)?!=?4?)

      BOOST_FAIL(?"Ouch..."?);?????????????//?#4?出錯后拋出

      if(?add(?2,2?)?!=?4?)?throw?"Ouch...";?//?#5?出錯后拋出

      BOOST_CHECK_MESSAGE(?add(?2,2?)?==?4,??//?#6?出錯后繼續(xù)

      "add(..)?result:?"?<

      BOOST_CHECK_EQUAL(?add(?2,2?),?4?);???//?#7?出錯后繼續(xù)

      }

      2.4.2????Fixture

      看一下測試用例Fixture。

      Fixture設置在測試用例執(zhí)行前被調(diào)用,F(xiàn)ixture拆除在測試用例完成執(zhí)行后被調(diào)用,與測試用例的執(zhí)行狀態(tài)無關。

      #define?BOOST_TEST_MODULE?example

      #include?

      struct?F?{

      F()?:?i(?0?)?{?BOOST_TEST_MESSAGE(?"setup?fixture"?);?}

      ~F()?????????{?BOOST_TEST_MESSAGE(?"teardown?fixture"?);?}

      int?i;

      };

      BOOST_FIXTURE_TEST_CASE(?test_case1,?F?)

      {

      BOOST_TEST(?i?==?1?);

      ++i;

      }

      BOOST_FIXTURE_TEST_CASE(?test_case2,?F?)

      {

      BOOST_CHECK_EQUAL(?i,?1?);

      }

      BOOST_AUTO_TEST_CASE(?test_case3?)

      {

      BOOST_TEST(?true?);

      }

      2.4.3????Suite

      在測試套件開始和結束聲明之間定義的測試單元成為測試Suite的成員。一個測試單元總是成為最近的測試Suite的成員。在測試文件范圍內(nèi)聲明的測試單元會成為主測試Suite的成員。測試Suite包含的深度沒有限制。

      #define?BOOST_TEST_MODULE?example

      #include?

      BOOST_AUTO_TEST_SUITE(?test_suite1?)

      BOOST_AUTO_TEST_CASE(?test_case1?)

      {

      BOOST_WARN(?sizeof(int)?

      }

      BOOST_AUTO_TEST_CASE(?test_case2?)

      {

      BOOST_REQUIRE_EQUAL(?1,?2?);

      BOOST_FAIL(?"Should?never?reach?this?line"?);

      }

      BOOST_AUTO_TEST_SUITE_END()

      BOOST_AUTO_TEST_SUITE(?test_suite2?)

      BOOST_AUTO_TEST_CASE(?test_case3?)

      {

      BOOST_CHECK(?true?);

      }

      BOOST_AUTO_TEST_CASE(?test_case4?)

      {

      BOOST_CHECK(?false?);

      }

      BOOST_AUTO_TEST_SUITE_END()

      3??????cppunit

      CppUnit是著名的JUnit框架的C++移植,用于單元測試。

      測試輸出為XML或文本格式,用于自動測試。

      3.1????官方網(wǎng)站

      http://cppunit.sourceforge.net/

      3.2????許可

      GNU Library or Lesser General Public License version 2.0 (LGPLv2)

      3.3????用法

      3.3.1????簡單的測試

      派生TestCase類。重寫方法runTest()。當你想檢查一個值時,調(diào)用CPPUNIT_ASSERT(bool),并傳入一個表達式。

      如果測試成功則為真。

      class?ComplexNumberTest?:?public?CppUnit::TestCase?{

      public:

      ComplexNumberTest(?std::string?name?)?:?CppUnit::TestCase(?name?)?{}

      void?runTest()?{

      CPPUNIT_ASSERT(?Complex?(10,?1)?==?Complex?(10,?1)?);

      CPPUNIT_ASSERT(?!(Complex?(1,?1)?==?Complex?(2,?2))?);

      }

      };

      3.3.2????Fixture

      Fixture是一組已知的對象,作為一組測試用例的基礎。在開發(fā)過程中進行測試時,F(xiàn)ixture非常方便。

      class?ComplexNumberTest?:?public?CppUnit::TestFixture?{

      private:

      Complex?*m_10_1,?*m_1_1,?*m_11_2;

      public:

      void?setUp()

      {

      m_10_1?=?new?Complex(?10,?1?);

      m_1_1?=?new?Complex(?1,?1?);

      m_11_2?=?new?Complex(?11,?2?);

      }

      void?tearDown()

      {

      delete?m_10_1;

      delete?m_1_1;

      delete?m_11_2;

      }

      };

      3.3.3????測試案例

      如何使用Fixture編寫和調(diào)用單個測試?

      這個過程有兩個步驟:

      1.?????????把測試用例寫成Fixture類的方法

      2.?????????創(chuàng)建一個運行該方法的TestCaller。

      class?ComplexNumberTest?:?public?CppUnit::TestFixture??{

      private:

      Complex?*m_10_1,?*m_1_1,?*m_11_2;

      public:

      void?setUp()

      {

      m_10_1?=?new?Complex(?10,?1?);

      m_1_1?=?new?Complex(?1,?1?);

      m_11_2?=?new?Complex(?11,?2?);

      }

      void?tearDown()

      {

      delete?m_10_1;

      delete?m_1_1;

      delete?m_11_2;

      }

      void?testEquality()

      {

      CPPUNIT_ASSERT(?*m_10_1?==?*m_10_1?);

      CPPUNIT_ASSERT(?!(*m_10_1?==?*m_11_2)?);

      }

      void?testAddition()

      {

      CPPUNIT_ASSERT(?*m_10_1?+?*m_1_1?==?*m_11_2?);

      }

      };

      可以像這樣為每個測試用例創(chuàng)建并運行實例:

      CppUnit::TestCaller?test(?"testEquality",

      &ComplexNumberTest::testEquality?);

      CppUnit::TestResult?result;

      test.run(?&result?);

      測試調(diào)用器構造函數(shù)的第二個參數(shù)是ComplexNumberTest上一個方法的地址。當測試調(diào)用器運行時,該特定方法將被運行。

      一旦你有幾個測試,就把它們組織成一個Suite。

      3.3.4????Suite

      CppUnit提供了一個TestSuite類,可以一起運行任意數(shù)量的TestCases。

      要創(chuàng)建一個由兩個或多個測試組成的套件,請執(zhí)行以下操作:

      CppUnit::TestSuite?suite;

      CppUnit::TestResult?result;

      suite.addTest(?new?CppUnit::TestCaller(

      "testEquality",

      &ComplexNumberTest::testEquality?)?);

      suite.addTest(?new?CppUnit::TestCaller(

      "testAddition",

      &ComplexNumberTest::testAddition?)?);

      suite.run(?&result?);

      3.3.5????TestRunner

      一旦你有了一個測試Suite,你就會想要運行它。CppUnit?提供了一些工具來定義要運行的Suite并顯示其結果。你可以通過一個靜態(tài)方法Suite來使你的Suite可以被TestRunner程序訪問,并返回一個測試Suite。

      #include?

      #include?"ExampleTestCase.h"

      #include?"ComplexNumberTest.h"

      int?main(?int?argc,?char?**argv)

      {

      CppUnit::TextUi::TestRunner?runner;

      白盒單元測試框架

      runner.addTest(?ExampleTestCase::suite()?);

      runner.addTest(?ComplexNumberTest::suite()?);

      runner.run();

      return?0;

      }

      TestRunner將運行這些測試。如果所有的測試都通過了,你會得到一條信息性消息。如果有任何測試失敗,你會得到以下信息:

      n??失敗的測試用例的名稱

      n??包含測試的源文件的名稱。

      n??發(fā)生故障的行號

      n??調(diào)用CPPUNIT_ASSERT()檢測到故障的所有文本。

      CppUnit可以區(qū)分失敗和錯誤。失敗是預期的,并通過斷言進行檢查。錯誤是未預料到的問題,比如零除法和其他由C++運行時或你的代碼引發(fā)的異常。

      4??????GoogleTest

      谷歌測試和模擬框架。

      4.1????官方網(wǎng)站

      https://github.com/google/googletest/

      4.2????許可

      BSD-3-Clause License

      4.3????主要特點

      n??一個xUnit測試框架。

      n??測試發(fā)現(xiàn)。

      n??豐富的斷言集。

      n??用戶定義的斷言。

      n??死亡測試。

      n??致命和非致命的失敗。

      n??值參數(shù)化測試。

      n??類型參數(shù)化測試。

      n??運行測試的各種選項。

      n??XML測試報告的生成。

      4.4????用法

      4.4.1????簡單的測試

      創(chuàng)建一個測試:

      1.?????????使用TEST()宏來定義和命名一個測試函數(shù)。這些都是不返回值的普通C++函數(shù)。

      2.?????????在這個函數(shù)中,連同你想包含的任何有效的C++語句,使用各種googletest斷言來檢查值。

      3.?????????測試的結果是由斷言決定的,如果測試中的任何斷言失敗(無論是致命的還是非致命的),或者測試崩潰,整個測試就會失敗。否則,就會成功。

      TEST(TestSuiteName,?TestName)?{

      ...?測試體?...

      }

      假設我們有一個簡單的函數(shù):

      int?Factorial(int?n);?//?返回n的階乘。

      這個功能的測試Suite:

      //?測試0的階乘。

      TEST(FactorialTest,?HandlesZeroInput)?{

      EXPECT_EQ(Factorial(0),?1);

      }

      //?測試正數(shù)的階乘。

      TEST(FactorialTest,?HandlesPositiveInput)?{

      EXPECT_EQ(Factorial(1),?1);

      EXPECT_EQ(Factorial(2),?2);

      EXPECT_EQ(Factorial(3),?6);

      EXPECT_EQ(Factorial(8),?40320);

      }

      4.4.2????Fixture

      要創(chuàng)建一個Fixture:

      1.?????????從::testing::Test衍生出一個類。用protected:定義它的主體,因為我們要從子類中訪問Fixture成員。

      2.?????????在類的內(nèi)部,聲明計劃使用的任何對象。

      3.?????????如果有必要,寫一個默認的構造函數(shù)或SetUp()函數(shù)來為每個測試準備對象。一個常見的錯誤是將SetUp()寫成Setup(),并加上一個小u--在C++11中使用override來確保拼寫正確。

      4.?????????如果有必要,寫一個析構器或TearDown()函數(shù)來釋放你在SetUp()中分配的任何資源。

      5.?????????如果需要,為你的測試定義子程序來共享。

      當使用Fixture時,使用TEST_F()代替TEST(),允許訪問測試Fixture中的對象和子程序。

      TEST_F(TestFixtureName,?TestName)?{

      ...?測試體?...

      }

      class?QueueTest?:?public?::testing::Test?{

      protected:

      void?SetUp()?override?{

      q1_.Enqueue(1);

      q2_.Enqueue(2);

      q2_.Enqueue(3);

      }

      //?void?TearDown()?override?{}

      Queue?q0_;

      Queue?q1_;

      Queue?q2_;

      TEST_F(QueueTest,?IsEmptyInitially)

      {

      EXPECT_EQ(q0_.size(),?0);

      }

      TEST_F(QueueTest,?DequeueWorks)

      {

      int?*n?=?q0_.Dequeue();

      EXPECT_EQ(n,?nullptr);

      n?=?q1_.Dequeue();

      ASSERT_NE(n,?nullptr);

      EXPECT_EQ(*n,?1);

      EXPECT_EQ(q1_.size(),?0);

      delete?n;

      n?=?q2_.Dequeue();

      ASSERT_NE(n,?nullptr);

      EXPECT_EQ(*n,?2);

      EXPECT_EQ(q2_.size(),?1);

      delete?n;

      }

      };

      當這些測試運行時,會發(fā)生以下情況:

      1.?????????googletest構建了一個QueueTest對象(我們稱它為t1)。

      2.?????????t1.SetUp()?初始化?t1。

      3.?????????第一個測試?(IsEmptyInitially)?在?t1?上運行。

      4.?????????t1.TearDown()?在測試結束后進行清理。

      5.?????????t1被銷毀。

      6.?????????在另一個QueueTest對象上重復上述步驟,這次運行DequeueWorks測試。

      5??????Catch2

      一個現(xiàn)代的、C++-native的、只引入頭文件的、用于單元測試、TDD和BDD的測試框架,使用C++11、C++14、C++17和更高版本(或Catch1.x分支的C++03)。

      Catch2是一個C++的多范式測試框架,也支持Objective-C(還有C)。它主要是作為一個單一的頭文件發(fā)布的,盡管某些擴展可能需要額外的頭文件。

      5.1????官方網(wǎng)站

      https://github.com/catchorg/Catch2

      5.2????許可

      Boost Software License 1.0

      5.3????主要特點

      n??快速且非常容易上手。只需下載catch.hpp,#include它就可以了。

      n??沒有外部依賴性。只要你能編譯C++11,并且有一個C++標準庫就可以使用。

      n??可把測試用例寫成:自注冊的,函數(shù)或者方法。

      n??將測試用例劃分為若干部分,每個部分都是孤立運行的(不需要Fixture)。

      n??使用BDD風格的Given-When-Then部分以及傳統(tǒng)的單元測試用例。

      n??只有一個核心斷言宏用于比較。標準的C/C++運算符被用于比較,但完整的表達式被分解,并且lhs和rhs值被記錄下來。

      n??測試使用自由形式的字符串來命名,不再使用嚴格標識符來命名。

      n??測試可以被標記,以方便運行特設的測試組。

      n??失敗可以融入Windows和Mac的調(diào)試器。

      n??輸出是通過模塊化的報告對象。包括基本的文本和XML報告器。可以很容易地添加自定義報告器。

      n??支持JUnit xml輸出,以便與第三方工具集成,如CI服務器。

      n??提供了一個默認的main()函數(shù),但你可以提供你自己的函數(shù)來實現(xiàn)完全控制(例如,集成到你自己的測試運行器GUI中)。

      n??提供了一個命令行解析器,你可以提供自己的main()函數(shù)。

      n??Catch可以測試自己。

      n??替代斷言宏報告失敗,但不中止測試用例。

      n??浮點容差比較是通過使用一個表達式的Approx()語法建立起來的。

      n??內(nèi)部和友好的宏被隔離,因此可以管理名稱沖突。

      n??匹配器

      5.4????用法

      獲取Catch2最簡單的方法就是下載最新的單頭版本。單一頭文件是由一組單獨的頭文件合并生成的,但它仍然只是頭文件中的普通源代碼。

      5.4.1????簡單的測試

      #define?CATCH_CONFIG_MAIN??//?這告訴Catch提供一個main()--只在一個cpp文件中這樣做

      #include?"catch.hpp"

      unsigned?int?Factorial(?unsigned?int?number?)?{

      return?number?<=?1???number?:?Factorial(number-1)*number;

      }

      TEST_CASE(?"Factorials?are?computed",?"[factorial]"?)?{

      REQUIRE(?Factorial(1)?==?1?);

      REQUIRE(?Factorial(2)?==?2?);

      REQUIRE(?Factorial(3)?==?6?);

      REQUIRE(?Factorial(10)?==?3628800?);

      }

      5.4.2????測試案例和區(qū)域

      大多數(shù)測試框架都有一個基于類的Fixture機制。也就是測試用例映射到類上的方法,常見的設置和拆解可以在setup()和?teardown()方法中進行(或者在C++等支持的構造函數(shù)/析構函數(shù))。

      Catch采用了一種不同的方法(與NUnit和xUnit都不同),它更自然地適合C++和C語言家族。

      TEST_CASE(?"vector的尺寸和容量可以調(diào)整",?"[vector]"?)?{

      std::vector?v(?5?);

      REQUIRE(?v.size()?==?5?);

      REQUIRE(?v.capacity()?>=?5?);

      SECTION(?"調(diào)整尺寸和容量"?)?{

      v.resize(?10?);

      REQUIRE(?v.size()?==?10?);

      REQUIRE(?v.capacity()?>=?10?);

      }

      SECTION(?"縮小尺寸但不改變?nèi)萘??)?{

      v.resize(?0?);

      REQUIRE(?v.size()?==?0?);

      REQUIRE(?v.capacity()?>=?5?);

      }

      SECTION(?"增加容量但不改變尺寸"?)?{

      v.reserve(?10?);

      REQUIRE(?v.size()?==?5?);

      REQUIRE(?v.capacity()?>=?10?);

      }

      SECTION(?"縮小尺寸或者容量"?)?{

      v.reserve(?0?);

      REQUIRE(?v.size()?==?5?);

      REQUIRE(?v.capacity()?>=?5?);

      }

      }

      對于每個SECTION,從一開始就執(zhí)行TEST_CASE,?所以當我們進入每個SECTION時,我們知道大小是5,容量至少是5。我們用頂層的REQUIREs強制執(zhí)行了這些要求。

      5.4.3????BDD-風格

      如果你適當?shù)孛愕臏y試用例和部分,你可以實現(xiàn)BDD式的規(guī)范結構。方案可以使用SCENARIO、GIVEN、WHEN和THEN宏來指定,這些宏分別映射到TEST_CASEs和SECTIONs。

      SCENARIO(?"vector的尺寸和容量可以調(diào)整",?"[vector]"?)?{

      GIVEN(?"含有一些項目的Vector"?)?{

      std::vector?v(?5?);

      REQUIRE(?v.size()?==?5?);

      REQUIRE(?v.capacity()?>=?5?);

      WHEN(?"尺寸增加"?)?{

      v.resize(?10?);

      THEN(?"尺寸和容量發(fā)生變化"?)?{

      REQUIRE(?v.size()?==?10?);

      REQUIRE(?v.capacity()?>=?10?);

      }

      }

      WHEN(?"尺寸減少了"?)?{

      v.resize(?0?);

      THEN(?"尺寸變化但是容量不變"?)?{

      REQUIRE(?v.size()?==?0?);

      REQUIRE(?v.capacity()?>=?5?);

      }

      }

      WHEN(?"增加容量"?)?{

      v.reserve(?10?);

      THEN(?"增加容量但不增加尺寸"?)?{

      REQUIRE(?v.size()?==?5?);

      REQUIRE(?v.capacity()?>=?10?);

      }

      }

      WHEN(?"減少容量"?)?{

      v.reserve(?0?);

      THEN(?"尺寸和容量都沒有改變"?)?{

      REQUIRE(?v.size()?==?5?);

      REQUIRE(?v.capacity()?>=?5?);

      }

      }

      }

      }

      6??????參考

      https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C++

      https://www.boost.org/libs/test/

      http://sourceforge.net/projects/cppunit/

      https://github.com/google/googletest/

      https://github.com/catchorg/Catch2

      https://github.com/onqtam/doctest

      https://github.com/martinmoene/lest

      https://github.com/etr/liblittletest

      https://github.com/mollismerx/elfspy

      https://github.com/gammasoft71/xtd.tunit

      http://banditcpp.github.io/bandit/

      單元測試

      版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。

      上一篇:excel表格生成圖片的方法步驟圖
      下一篇:excel表格內(nèi)文字180旋轉(zhuǎn)的方法教程
      相關文章
      国产成人亚洲精品狼色在线| 爱情岛论坛亚洲品质自拍视频网站| 日韩亚洲国产综合高清| 亚洲国产精品成人精品无码区 | 中文亚洲AV片在线观看不卡| 亚洲AV中文无码乱人伦在线视色 | 久久夜色精品国产嚕嚕亚洲av| 亚洲av无码天堂一区二区三区 | 777亚洲精品乱码久久久久久| 久久香蕉国产线看观看亚洲片| 亚洲av无码天堂一区二区三区 | 亚洲免费在线观看| 日本系列1页亚洲系列| 精品久久久久久久久亚洲偷窥女厕| 亚洲αⅴ无码乱码在线观看性色| 亚洲精品成人区在线观看| 亚洲第一香蕉视频| 久久亚洲精品成人| 久久亚洲中文字幕精品有坂深雪| 亚洲欧洲日韩不卡| 亚洲精品综合久久中文字幕| 亚洲日韩中文字幕| avtt天堂网手机版亚洲| 亚洲成年人啊啊aa在线观看| 亚洲乱码中文字幕手机在线 | 亚洲精品亚洲人成在线观看麻豆| 亚洲精品福利网站| 亚洲专区中文字幕| 国产精品亚洲专区在线观看| 亚洲无人区码一二三码区别图片| 欧美亚洲精品一区二区| 亚洲成AV人网址| 中文字幕精品亚洲无线码二区| 亚洲人成网亚洲欧洲无码久久| 日韩亚洲一区二区三区| 久久精品国产亚洲av高清漫画| 亚洲国产精品无码久久久| 亚洲一区电影在线观看| 亚洲熟妇自偷自拍另欧美| 亚洲1区2区3区精华液| 亚洲精品456播放|