UT编写记录
UT編寫規則?https://zhuanlan.zhihu.com/p/424117483
1、遵循AIR原則
A(automation,自動化):單元測試應該是全自動執行的,并且非交互。
I (Independence,獨立性):單元測試用例之間決不能互相調用,也不能依賴執行的先后次序。
--------反例:method2需要依賴method1的執行,將執行結果作為method2的輸入。
R(Repeatable,可重復):單元測試是可以重復執行的,不能受到外界環境的影響。
2、結構
a、 單元測試代碼建議有一個新的文件目錄
說明:源碼構建時會跳過此目錄,而單元測試框架默認是掃描此目錄。
b、測試用例的包以及結構,需要保持和被測的包以及結構一致。
3、命名
a、測試類命名規范:被測試的業務+Test、被測試的接口+Test、被測試的類+Test
b、測試用例命名規范:測試用例命名應做到簡潔全面,可描述。避免使用test1、test2沒有含義的名稱。其次需要有必要的函數方法注釋。
4、禁止使用
a、不要在單元測試中使用Thread.sleep
UT測試類的封裝
gtest 中Setup TearDown SetUpTestCase和TearDownTestCase 的區別_惹不起的程咬金的博客-CSDN博客_gtest teardown
使用TEST_F前需要創建一個固件類,繼承于::testing::Test類。在類內部使用public或者protected描述其成員,為了保證實際執行的測試子類可以使用其成員變量。在構造函數或者繼承于::testing::Test類中的SetUp方法中,可以實現我們需要構造的數據。在析構函數或者繼承于::testing::Test類中的TearDown方法中,可以實現一些資源釋放的代碼?!皹嬙旌瘮?析構函數”和“SetUp/TearDown”的選擇,對于什么時候選擇哪對,沒有統一的標準。一般來說就是構造/析構函數里忌諱做什么就不要在里面做,比如拋出異常等
全局:
class Test:public testing::Environment { public:virtual void SetUp() ;virtual void TearDown()? ; }; void ?Test::SetUp() { } void Test::TearDown() { } int main(int argc, char* argv[]) {testing::AddGlobalTestEnvironment(new Test);testing::GTEST_FLAG(output) = "xml:test.xml";testing::InitGoogleTest(&argc, argv);RUN_ALL_TESTS();return 0; }測試套件:
頭文件:
class Test:public::testing::Test { public:static void SetUpTestCase() ;static void TearDownTestCase() ; };打樁方式?
打樁的目的主要有:隔離、補齊、控制。隔離是指將測試任務從產品項目中分離出來,使之能夠獨立編譯、鏈接,并獨立運行。隔離的基本方法就是打樁,將測試任務之外的,并且與測試任務相關的代碼,用樁來代替,從而實現分離測試任務。例如函數A調用了函數B,函數B又調用了函數C和D,如果函數B用樁來代替,函數A就可以完全割斷與函數C和D的關系(隔離了A和C、D,而不是隔離A和B)。
-
補齊是指用樁來代替未實現的代碼,例如,函數A調用了函數B,而函數B由其他程序員編寫,且未實現,那么,可以用樁來代替函數B,使函數A能夠運行并測試。補齊在并行開發中很常用。
-
控制是指在測試時,人為設定相關代碼的行為,使之符合測試需求
一、stub打樁
Stub可在gitHub上下載其源碼,以源碼方式集成,下載地址https://github.com/coolxv/cpp-stub ,提供的頭文件名稱為stub.h,需要注意的是,如果也引用Mockcpp方式,Mockcpp類也提供了stub頭文件,所以需要在stub的stub.h頭文件增加命名空間,防止兩者沖突。
頭文件提供兩個函數,set,reset 函數
void set(T addr, S addr_stub)
addr為需要打樁的函數地址,addr_stub為需要替換的函數
void reset((T addr);
將被打樁的函數恢復。
(1)普通函數打樁(非static) #include<iostream> #include "stub.h" using namespace std; int foo(int a) { cout<<"I am foo"<<endl;return 0; } int foo_stub(int a) { cout<<"I am foo_stub"<<endl;return 0; } int main() {Stub stub;stub.set(foo, foo_stub);foo(1);return 0; } (2)實例成員函數打樁 include<iostream> #include "stub.h" using namespace std; class A{int i; public:int foo(int a){cout<<"I am A_foo"<<endl;return 0;} }; int foo_stub(void* obj, int a) { A* o= (A*)obj;cout<<"I am foo_stub"<<endl;return 0; } int main() {Stub stub;stub.set(ADDR(A,foo), foo_stub);A a;a.foo(1);return 0; } 例:重載函數打樁 (o_xpath_bool 為需要被打樁的函數) namespace TinyXPath { ? ?extern TIXML_API bool o_xpath_bool (const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr); ? ?extern TIXML_API bool o_xpath_bool (const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr, bool & o_res) } bool stub_o_xpath_bool (const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr) { ? ? ?std::cout<<" stub_o_xpath_bool"; ? ? return true; } TEST(SipXMLParserTest, ParseInfoArrived) { ..... bool (*func)(const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr); func= TinyXPath::o_xpath_bool; mystub::Stub stub_path_bool; stub_path_bool.set(func,stub_o_xpath_bool); ...... }stub打樁在虛函數打樁比較麻煩,需要計算虛函數的地址?
虛函數地址的計算:https://blog.csdn.net/qq_42956179/article/details/105428342
二、Mockcpp打樁
Stub因只要能獲取到函數地址,即可對函數進行打樁,覆蓋返回較廣,但每一個打樁的函數需要寫一個合適的替換函數,比較麻煩,且某些場景無法滿足,如測試函數中多次調用到同一個需要打樁的函,如下例中,如采用stub方式對test打樁,針對test函數不同入參,難以編寫一個合適的打樁函數。
int func() { int a = test(1); int b = test(2); return a + b; }Mockcpp 在C函數打樁相對于Stub函數打樁較為方便。
C函數與靜態函數打樁
MOCKCPP對C函數及靜態函數打樁采用MOCKER方式
(1)mock規范:每個MOCKER(function)開始,跟一系列的.stubs、.with、.will等的內容的整體,稱為一個mock規范。
(2)核心關鍵字:指stubs/defaults/expects/before/with/after/will/then/id等這些直接跟在點后面的關鍵字。
(3)擴展關鍵字:指once()/eq()/check()/returnValue()/repeat()等這些作為核心關鍵字參數的關鍵字。(AMOCK的follow在mockcpp中是check)
例:MOCKER(UCSP::bIsDomain).stubs().will(returnValue(true));
類函數打樁
采用的是MockObject關鍵字,無法對普通成員函數打樁。
對虛函數打樁
例:
struct Base0{virtual int ?base00() = 0;virtual bool base01(int) const = 0;virtual ~Base0() {}};struct Base1{virtual void base10() = 0;virtual long base11(const std::string&) const = 0;virtual int ?base12() const = 0;virtual ~Base1() {}};struct Interface: public Base0, public Base1{virtual const std::string& a() {}virtual void b(bool) {}};void testShouldBeAbleReturnTheExpectedValue(){MockObject<Interface> mock;mock.method(&Interface::base00).stubs().will(returnValue(20));TS_ASSERT_EQUALS(20, mock->base00());TS_ASSERT_EQUALS(20, mock->base00());TS_ASSERT_EQUALS(20, mock->base00());}void testShouldBeAbleToSupportMultipleThenSpecification(){MockObject<Interface> mock;mock.method(&Interface::base00).stubs().will(returnValue(20)).then(returnValue(10)).then(returnValue(1)).then(returnValue(5));TS_ASSERT_EQUALS(20, mock->base00());TS_ASSERT_EQUALS(10, mock->base00());TS_ASSERT_EQUALS(1, mock->base00());TS_ASSERT_EQUALS(5, mock->base00());TS_ASSERT_EQUALS(5, mock->base00());}? 調用GlobalMockObject::verify()釋放
GitHub - ez8-co/emock: 🐞 下一代C/C++跨平臺mock庫 (Next generation cross-platform mock library for C/C++)?github上還提及了一種方法 emock,打樁時發現有報錯,未深究.
UT覆蓋率統計
編譯環境為linux,采用的QT平臺
編譯命令:?
Pro文件中添加編譯屬性
QMAKE_CXXFLAGS? += -fprofile-arcs -ftest-coverage
QMAKE_LFLAGS+=-fprofile-arcs -ftest-coverage
另添加
QMAKE_CXXFLAGS +=-fno-inline
QMAKE_LFLAGS +=-fno-inline
這兩行屬性主要是禁止內聯,可以獲取內聯函數地址,從而便于打樁
運行UT工程后會生成.gcna文件
統計覆蓋率命令,
使用的是gcov工具
具體命令:
cd ../tmp
lcov -c -d ./ -o test.info --rc lcov_branch_coverage=1 --rc? lcov_excl_br_line="new|delete|malloc|free|c_str|.value"
lcov -r test.info "*/3rd/*"? -o MediaSDK.info --rc lcov_branch_coverage=1
/lcov -r test.info -o MediaSDK.info --rc lcov_branch_coverage=1
genhtml --branch-coverage MediaSDK.info --output-directory result
?--rc? lcov_excl_br_line參數表示去除不想要計算的分支關鍵字
lcov -r去除不想覆蓋的文件目錄
總結
- 上一篇: PHP100视频教程(2012-2013
- 下一篇: MAC 安装oracle instant