• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Google單元測試框架(轉(zhuǎn))

            Google Test是Google C++ Testing Framework的一種非正式的稱謂,是google最近發(fā)布的一個開源C++測試框架。

            Google測試框架是在不同平臺上(Linux,Mac OS X,Windows,Cygwin,Windows CE和Symbian)為編寫C++測試而生成的。它是基于xUnit架構(gòu)的測試框架,支持自動發(fā)現(xiàn)測試,豐富的斷言集,用戶定義的斷言,death測試,致命與非致命的失敗,類型參數(shù)化測試,各類運行測試的選項和XML的測試報告。

            gtest的官方網(wǎng)站是:http://code.google.com/p/googletest/

            從官方的使用文檔里,你幾乎可以獲得你想要的所有東西
            http://code.google.com/p/googletest/wiki/GoogleTestPrimer
            http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide

            一、第一個Demo:
            先來寫一個被測試函數(shù):

            intFoo(int a,int b)
            {
            if(a==0||b==0)
            {
            throw "don'tdothat";
            }

            int c=a%b;
            if(c==0)
            return b;
            return Foo(b,c);
            }

            上面的函數(shù)是用來求最大公約數(shù)的。下面我們就來編寫一個簡單的測試案例。
            #include<gtest/gtest.h>

            TEST(FooTest,HandleNoneZeroInput)
            {
            EXPECT_EQ(2,Foo(4,10));
            EXPECT_EQ(6,Foo(30,18));
            }
            我們使用了TEST這個宏,它有兩個參數(shù),官方的對這兩個參數(shù)的解釋為:[TestCaseName,TestName],而我對這兩個參數(shù)的定義是:[TestSuiteName,TestCaseName],在下一篇我們再來看為什么這樣定義。

            對檢查點的檢查,我們上面使用到了EXPECT_EQ這個宏,這個宏用來比較兩個數(shù)字是否相等。Google還包裝了一系列EXPECT_* 和ASSERT_*的宏,而EXPECT系列和ASSERT系列的區(qū)別是:

            1. EXPECT_* 失敗時,案例繼續(xù)往下執(zhí)行。

            2. ASSERT_* 失敗時,案例終止運行。

            為了讓我們的案例運行起來,我們還需要在main函數(shù)中添加如下代碼:

            int_tmain(intargc,_TCHAR*argv[])
            {
            testing::InitGoogleTest(
            &argc,argv);
            return RUN_ALL_TESTS();
            }

            “testing::InitGoogleTest(&argc, argv);” :gtest的測試案例允許接收一系列的命令行參數(shù),因此,我們將命令行參數(shù)傳遞給gtest,進(jìn)行一些初始化操作。gtest的命令行參數(shù)非常豐富,在后面我們也會詳細(xì)了解到。

            “RUN_ALL_TESTS()” :運行所有測試案例

            二、斷言
            gtest中,斷言的宏可以理解為分為兩類,一類是ASSERT系列,一類是EXPECT系列。一個直觀的解釋就是:
            1. ASSERT_* 系列的斷言,當(dāng)檢查點失敗時,退出當(dāng)前案例的執(zhí)行。

            2. EXPECT_* 系列的斷言,當(dāng)檢查點失敗時,繼續(xù)往下執(zhí)行。

            布爾值檢查

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition is true
            ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition is false

            數(shù)值型數(shù)據(jù)檢查

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_EQ(expected, actual); EXPECT_EQ(expected, actual); expected == actual
            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

            字符串檢查

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_STREQ(expected_str, actual_str); EXPECT_STREQ(expected_str, actual_str); the two C strings have the same content
            ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2); the two C strings have different content
            ASSERT_STRCASEEQ(expected_str, actual_str); EXPECT_STRCASEEQ(expected_str, actual_str); the two C strings have the same content, ignoring case
            ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2); the two C strings have different content, ignoring case
            *STREQ*和*STRNE*同時支持char*和wchar_t*類型的,*STRCASEEQ*和*STRCASENE*卻只接收char*,估計是不常用吧。下面是幾個例子:

            TEST(StringCmpTest,Demo)
            {
            char*pszCoderZh="CoderZh";
            wchar_t
            *wszCoderZh=L"CoderZh";
            std::stringstrCoderZh
            ="CoderZh";
            std::wstringwstrCoderZh
            =L"CoderZh";

            EXPECT_STREQ(
            "CoderZh",pszCoderZh);
            EXPECT_STREQ(L
            "CoderZh",wszCoderZh);

            EXPECT_STRNE(
            "CnBlogs",pszCoderZh);
            EXPECT_STRNE(L
            "CnBlogs",wszCoderZh);

            EXPECT_STRCASEEQ(
            "coderzh",pszCoderZh);
            //EXPECT_STRCASEEQ(L"coderzh",wszCoderZh);不支持

            EXPECT_STREQ(
            "CoderZh",strCoderZh.c_str());
            EXPECT_STREQ(L
            "CoderZh",wstrCoderZh.c_str());
            }

            顯示返回成功或失敗

            直接返回成功:SUCCEED();

            返回失敗:

            Fatal assertion Nonfatal assertion
            FAIL(); ADD_FAILURE();

            異常檢查

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_THROW(statement, exception_type); EXPECT_THROW(statement, exception_type); statement throws an exception of the given type
            ASSERT_ANY_THROW(statement); EXPECT_ANY_THROW(statement); statement throws an exception of any type
            ASSERT_NO_THROW(statement); EXPECT_NO_THROW(statement); statement doesn't throw any exception

            例如:

             

            intFoo(inta,intb)
            {
            if(a==0||b==0)
            {
            throw"don'tdothat";
            }

            intc
            =a%b;
            if(c==0)
            returnb;
            returnFoo(b,c);
            }


            TEST(FooTest,HandleZeroInput)
            {
            EXPECT_ANY_THROW(Foo(
            10,0));
            EXPECT_THROW(Foo(
            0,5),char*);
            }

            Predicate Assertions

            在使用EXPECT_TRUE或ASSERT_TRUE時,有時希望能夠輸出更加詳細(xì)的信息,比如檢查一個函數(shù)的返回值TRUE還是FALSE時,希望能夠輸出傳入的參數(shù)是什么,以便失敗后好跟蹤。因此提供了如下的斷言:

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_PRED1(pred1, val1); EXPECT_PRED1(pred1, val1); pred1(val1) returns true
            ASSERT_PRED2(pred2, val1, val2); EXPECT_PRED2(pred2, val1, val2); pred2(val1, val2) returns true
            ... ... ...

            Google人說了,他們只提供<=5個參數(shù)的,如果需要測試更多的參數(shù),直接告訴他們。下面看看這個東西怎么用。

            boolMutuallyPrime(intm,intn)
            {
            returnFoo(m,n)
            >1;
            }


            TEST(PredicateAssertionTest,Demo)
            {
            intm
            =5,n=6;
            EXPECT_PRED2(MutuallyPrime,m,n);
            }

            當(dāng)失敗時,返回錯誤信息:

            error: MutuallyPrime(m, n) evaluates to false, where
            m evaluates to 5
            n evaluates to 6

            如果對這樣的輸出不滿意的話,還可以自定義輸出格式,通過如下:

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_PRED_FORMAT1(pred_format1, val1);` EXPECT_PRED_FORMAT1(pred_format1, val1); pred_format1(val1) is successful
            ASSERT_PRED_FORMAT2(pred_format2, val1, val2); EXPECT_PRED_FORMAT2(pred_format2, val1, val2); pred_format2(val1, val2) is successful
            ... ...

            用法示例:

             

            testing::AssertionResultAssertFoo(constchar*m_expr,constchar*n_expr,constchar*k_expr,intm,intn,intk){
            if(Foo(m,n)==k)
            returntesting::AssertionSuccess();
            testing::Messagemsg;
            msg
            <<m_expr<<""<<n_expr<<"的最大公約數(shù)應(yīng)該是:"<<Foo(m,n)<<"而不是:"<<k_expr;
            returntesting::AssertionFailure(msg);
            }


            TEST(AssertFooTest,HandleFail)
            {
            EXPECT_PRED_FORMAT3(AssertFoo,
            3,6,2);
            }

            失敗時,輸出信息:

            error: 3 和 6 的最大公約數(shù)應(yīng)該是:3 而不是:2

            是不是更溫馨呢,呵呵。

            浮點型檢查

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_FLOAT_EQ(expected, actual); EXPECT_FLOAT_EQ(expected, actual); the two float values are almost equal
            ASSERT_DOUBLE_EQ(expected, actual); EXPECT_DOUBLE_EQ(expected, actual); the two double values are almost equal

            對相近的兩個數(shù)比較:

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_NEAR(val1, val2, abs_error); EXPECT_NEAR(val1, val2, abs_error); the difference between val1 and val2 doesn't exceed the given absolute error

            同時,還可以使用:

            EXPECT_PRED_FORMAT2(testing::FloatLE,val1,val2);
            EXPECT_PRED_FORMAT2(testing::DoubleLE,val1,val2);


            Windows HRESULT assertions

            Fatal assertion Nonfatal assertion Verifies
            ASSERT_HRESULT_SUCCEEDED(expression); EXPECT_HRESULT_SUCCEEDED(expression); expression is a success HRESULT
            ASSERT_HRESULT_FAILED(expression); EXPECT_HRESULT_FAILED(expression); expression is a failure HRESULT

            例如:

            CComPtrshell;
            ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L
            "Shell.Application"));
            CComVariantempty;
            ASSERT_HRESULT_SUCCEEDED(shell
            ->ShellExecute(CComBSTR(url),empty,empty,empty,empty));

            類型檢查

            類型檢查失敗時,直接導(dǎo)致代碼編不過,難得用處就在這?看下面的例子:

             

            template<typenameT>
            classFooType
            {
            public:
            voidBar()
            {
              testing::StaticAssertTypeEq
            <int,T>();
            }

            }
            ;

            TEST(TypeAssertionTest,Demo)
            {
            FooType
            <bool>fooType;
            fooType.Bar();
            }


            三、事件

            gtest提供了多種事件機(jī)制,非常方便我們在案例之前或之后做一些操作。總結(jié)一下gtest的事件一共有3種:

            1. 全局的,所有案例執(zhí)行前后。

            2. TestSuite級別的,在某一批案例中第一個案例前,最后一個案例執(zhí)行后。

            3. TestCae級別的,每個TestCase前后。
            全局事件

            要實現(xiàn)全局事件,必須寫一個類,繼承testing::Environment類,實現(xiàn)里面的SetUp和TearDown方法。

            1. SetUp()方法在所有案例執(zhí)行前執(zhí)行

            2. TearDown()方法在所有案例執(zhí)行后執(zhí)行

            class FooEnvironment:public testing::Environment
            {
            public:
            virtual void SetUp()
            {
            std::cout
            <<"FooFooEnvironmentSetUP"<<std::endl;
            }

            virtual void TearDown()
            {
            std::cout
            <<"FooFooEnvironmentTearDown"<<std::endl;
            }

            }
            ;
            當(dāng)然,這樣還不夠,我們還需要告訴gtest添加這個全局事件,我們需要在main函數(shù)中通過testing::AddGlobalTestEnvironment方法將事件掛進(jìn)來,也就是說,我們可以寫很多個這樣的類,然后將他們的事件都掛上去。
            int _tmain(int argc,_TCHAR* argv[])
            {
            testing::AddGlobalTestEnvironment(newFooEnvironment);
            testing::InitGoogleTest(
            &argc,argv);
            return RUN_ALL_TESTS();
            }
            TestSuite事件

            我們需要寫一個類,繼承testing::Test,然后實現(xiàn)兩個靜態(tài)方法

            1. SetUpTestCase() 方法在第一個TestCase之前執(zhí)行
            2.
            TearDownTestCase() 方法在最后一個TestCase之后執(zhí)行
            class FooTest:public testing::Test{
            protected:
            static void SetUpTestCase(){
            shared_resource_
            =new;
            }

            static void TearDownTestCase(){
            delete shared_resource_;
            shared_resource_
            =NULL;
            }

            //Someexpensiveresourcesharedbyalltests.
            staticT* shared_resource_;
            }
            ;
            在編寫測試案例時,我們需要使用TEST_F這個宏,第一個參數(shù)必須是我們上面類的名字,代表一個TestSuite。
            TEST_F(FooTest,Test1)
            {
            //youcanrefertoshared_resourcehere
            }
            TEST_F(FooTest,Test2)
            {
            //youcanrefertoshared_resourcehere
            }
            TestCase事件

            TestCase事件是掛在每個案例執(zhí)行前后的,實現(xiàn)方式和上面的幾乎一樣,不過需要實現(xiàn)的是SetUp方法和TearDown方法:

            1. SetUp()方法在每個TestCase之前執(zhí)行

            2. TearDown()方法在每個TestCase之后執(zhí)行

            class FooCalcTest:public testing::Test
            {
            protected:
            virtual void SetUp()
            {
            m_foo.Init();
            }

            virtual void TearDown()
            {
            m_foo.Finalize();
            }


            FooCalc m_foo;
            }
            ;

            TEST_F(FooCalcTest,HandleNoneZeroInput)
            {
            EXPECT_EQ(
            4,m_foo.Calc(12,16));
            }


            TEST_F(FooCalcTest,HandleNoneZeroInput_Error)
            {
            EXPECT_EQ(
            5,m_foo.Calc(12,16));
            }


            四、參數(shù)化

            在設(shè)計測試案例時,經(jīng)常需要考慮給被測函數(shù)傳入不同的值的情況。我們之前的做法通常是寫一個通用方法,然后編寫在測試案例調(diào)用它。即使使用了通用方法,這樣的工作也是有很多重復(fù)性的,程序員都懶,都希望能夠少寫代碼,多復(fù)用代碼。Google的程序員也一樣,他們考慮到了這個問題,并且提供了一個靈活的參數(shù)化測試的方案。

            舊的方案

            為了對比,我還是把舊的方案提一下。首先我先把被測函數(shù)IsPrime帖過來(在gtest的example1.cc中),這個函數(shù)是用來判斷傳入的數(shù)值是否為質(zhì)數(shù)的。

            // Returns true iff n is a prime number.
            bool IsPrime(int n)
            {
                
            // Trivial case 1: small numbers
                if (n <= 1return false;

                
            // Trivial case 2: even numbers
                if (n % 2 == 0return n == 2;

                
            // Now, we have that n is odd and n >= 3.

                
            // Try to divide n by every odd number i, starting from 3
                for (int i = 3; ; i += 2{
                    
            // We only have to try i up to the squre root of n
                    if (i > n/i) break;

                    
            // Now, we have i <= n/i < n.
                    
            // If n is divisible by i, n is not prime.
                    if (n % i == 0return false;
                }

                
            // n has no integer factor in the range (1, n), and thus is prime.
                return true;
            }

            假如我要編寫判斷結(jié)果為True的測試案例,我需要傳入一系列數(shù)值讓函數(shù)IsPrime去判斷是否為True(當(dāng)然,即使傳入再多值也無法確保函數(shù)正確,呵呵),因此我需要這樣編寫如下的測試案例:

            TEST(IsPrimeTest, HandleTrueReturn)
            {
                EXPECT_TRUE(IsPrime(
            3));
                EXPECT_TRUE(IsPrime(
            5));
                EXPECT_TRUE(IsPrime(
            11));
                EXPECT_TRUE(IsPrime(
            23));
                EXPECT_TRUE(IsPrime(
            17));
            }

            我們注意到,在這個測試案例中,我至少復(fù)制粘貼了4次,假如參數(shù)有50個,100個,怎么辦?同時,上面的寫法產(chǎn)生的是1個測試案例,里面有5個檢查點,假如我要把5個檢查變成5個單獨的案例,將會更加累人。

            接下來,就來看看gtest是如何為我們解決這些問題的。
            使用參數(shù)化后的方案

            1. 告訴gtest你的參數(shù)類型是什么

            你必須添加一個類,繼承testing::TestWithParam<T>,其中T就是你需要參數(shù)化的參數(shù)類型,比如上面的例子,我需要參數(shù)化一個int型的參數(shù)

             

            class IsPrimeParamTest : public::testing::TestWithParam<int>
            {

            }
            ;

            告訴gtest你拿到參數(shù)的值后,具體做些什么樣的測試

            這里,我們要使用一個新的宏(嗯,挺興奮的):TEST_P,關(guān)于這個"P"的含義,Google給出的答案非常幽默,就是說你可以理解為”parameterized" 或者 "pattern"。我更傾向于parameterized"的解釋,呵呵。在TEST_P宏里,使用GetParam()獲取當(dāng)前的參數(shù)的具體值

            TEST_P(IsPrimeParamTest, HandleTrueReturn)
            {
                
            int n =  GetParam();
                EXPECT_TRUE(IsPrime(n));
            }

            嗯,非常的簡潔!
            告訴gtest你想要測試的參數(shù)范圍是什么

             使用INSTANTIATE_TEST_CASE_P這宏來告訴gtest你要測試的參數(shù)范圍:

            INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(35112317));

            第一個參數(shù)是測試案例的前綴,可以任意取。

            第二個參數(shù)是測試案例的名稱,需要和之前定義的參數(shù)化的類的名稱相同,如:IsPrimeParamTest

            第三個參數(shù)是可以理解為參數(shù)生成器,上面的例子使用test::Values表示使用括號內(nèi)的參數(shù)。Google提供了一系列的參數(shù)生成的函數(shù):

            Range(begin, end[, step]) 范圍在begin~end之間,步長為step,不包括end
            Values(v1, v2, ..., vN) v1,v2到vN的值
            ValuesIn(container) and ValuesIn(begin, end) 從一個C類型的數(shù)組或是STL容器,或是迭代器中取值
            Bool() false 和 true 兩個值
            Combine(g1, g2, ..., gN)

            這個比較強悍,它將g1,g2,...gN進(jìn)行排列組合,g1,g2,...gN本身是一個參數(shù)生成器,每次分別從g1,g2,..gN中各取出一個值,組合成一個元組(Tuple)作為一個參數(shù)。

            說明:這個功能只在提供了<tr1/tuple>頭的系統(tǒng)中有效。gtest會自動去判斷是否支持tr/tuple,如果你的系統(tǒng)確實支持,而gtest判斷錯誤的話,你可以重新定義宏GTEST_HAS_TR1_TUPLE=1

            參數(shù)化后的測試案例名

            因為使用了參數(shù)化的方式執(zhí)行案例,我非常想知道運行案例時,每個案例名稱是如何命名的。
             命名規(guī)則大概為:

            prefix/test_case_name.test.name/index
            類型參數(shù)化

            gtest還提供了應(yīng)付各種不同類型的數(shù)據(jù)時的方案,以及參數(shù)化類型的方案。我個人感覺這個方案有些復(fù)雜。首先要了解一下類型化測試,就用gtest里的例子了。

            首先定義一個模版類,繼承testing::Test:

             

            template <typename T>
            class FooTest : public testing::Test {
             
            public:
              
              typedef std::list
            <T> List;
              
            static T shared_;
              T value_;
            }
            ;

            接著我們定義需要測試到的具體數(shù)據(jù)類型,比如下面定義了需要測試char,int和unsigned int :

            typedef testing::Types<charint, unsigned int> MyTypes;
            TYPED_TEST_CASE(FooTest, MyTypes);

            又是一個新的宏,來完成我們的測試案例,在聲明模版的數(shù)據(jù)類型時,使用TypeParam

             

            TYPED_TEST(FooTest, DoesBlah) {
              
            // Inside a test, refer to the special name TypeParam to get the type
              
            // parameter.  Since we are inside a derived class template, C++ requires
              
            // us to visit the members of FooTest via 'this'.
              TypeParam n = this->value_;

              
            // To visit static members of the fixture, add the 'TestFixture::'
              
            // prefix.
              n += TestFixture::shared_;

              
            // To refer to typedefs in the fixture, add the 'typename TestFixture::'
              
            // prefix.  The 'typename' is required to satisfy the compiler.
              typename TestFixture::List values;
              values.push_back(n);
              
            }

            上面的例子看上去也像是類型的參數(shù)化,但是還不夠靈活,因為需要事先知道類型的列表。gtest還提供一種更加靈活的類型參數(shù)化的方式,允許你在完成測試的邏輯代碼之后再去考慮需要參數(shù)化的類型列表,并且還可以重復(fù)的使用這個類型列表。下面也是官方的例子:

            template <typename T>
            class FooTest : public testing::Test {
              
            };

            TYPED_TEST_CASE_P(FooTest);

            接著又是一個新的宏TYPED_TEST_P類完成我們的測試案例:

            TYPED_TEST_P(FooTest, DoesBlah) {
              
            // Inside a test, refer to TypeParam to get the type parameter.
              TypeParam n = 0;
              
            }

            TYPED_TEST_P(FooTest, HasPropertyA) {  }

            接著,我們需要我們上面的案例,使用REGISTER_TYPED_TEST_CASE_P宏,第一個參數(shù)是testcase的名稱,后面的參數(shù)是test的名稱

            REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);

            接著指定需要的類型列表:

            typedef testing::Types<charint, unsigned int> MyTypes;
            INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);

            這種方案相比之前的方案提供更加好的靈活度,當(dāng)然,框架越靈活,復(fù)雜度也會隨之增加。

            五、死亡測試
            死亡測試”名字比較恐怖,這里的“死亡”指的的是程序的崩潰。通常在測試過程中,我們需要考慮各種各樣的輸入,有的輸入可能直接導(dǎo)致程序崩潰,這時我們就需要檢查程序是否按照預(yù)期的方式掛掉,這也就是所謂的“死亡測試”。gtest的死亡測試能做到在一個安全的環(huán)境下執(zhí)行崩潰的測試案例,同時又對崩潰結(jié)果進(jìn)行驗證。
            使用的宏
            Fatal assertion Nonfatal assertion Verifies
            ASSERT_DEATH(statement, regex`); EXPECT_DEATH(statement, regex`); statement crashes with the given error
            ASSERT_EXIT(statement, predicate, regex`); EXPECT_EXIT(statement, predicate, regex`); statement exits with the given error and its exit code matches predicate

            由于有些異常只在Debug下拋出,因此還提供了*_DEBUG_DEATH,用來處理Debug和Realease下的不同。

            、*_DEATH(statement, regex`)

            1. statement是被測試的代碼語句

            2. regex是一個正則表達(dá)式,用來匹配異常時在stderr中輸出的內(nèi)容

            如下面的例子:

            void Foo()
            {
                
            int *pInt = 0;
                
            *pInt = 42 ;
            }

            TEST(FooDeathTest, Demo)
            {
                EXPECT_DEATH(Foo(), 
            "");
            }

            重要:編寫死亡測試案例時,TEST的第一個參數(shù),即testcase_name,請使用DeathTest后綴。原因是gtest會優(yōu)先運行死亡測試案例,應(yīng)該是為線程安全考慮。

            *_EXIT(statement, predicate, regex`)

            1. statement是被測試的代碼語句

            2. predicate 在這里必須是一個委托,接收int型參數(shù),并返回bool。只有當(dāng)返回值為true時,死亡測試案例才算通過。gtest提供了一些常用的predicate:

            testing::ExitedWithCode(exit_code)


            如果程序正常退出并且退出碼與exit_code相同則返回 true

            testing::KilledBySignal(signal_number)  // Windows下不支持

             
            如果程序被signal_number信號kill的話就返回true

             regex是一個正則表達(dá)式,用來匹配異常時在stderr中輸出的內(nèi)容

            這里, 要說明的是,*_DEATH其實是對*_EXIT進(jìn)行的一次包裝,*_DEATH的predicate判斷進(jìn)程是否以非0退出碼退出或被一個信號殺死。

            例子:

            TEST(ExitDeathTest, Demo)
            {
                EXPECT_EXIT(_exit(
            1),  testing::ExitedWithCode(1),  "");
            }

            、*_DEBUG_DEATH

            先來看定義:

            #ifdef NDEBUG

            #define EXPECT_DEBUG_DEATH(statement, regex) \
              
            do { statement; } while (false)

            #define ASSERT_DEBUG_DEATH(statement, regex) \
              
            do { statement; } while (false)

            #else

            #define EXPECT_DEBUG_DEATH(statement, regex) \
              EXPECT_DEATH(statement, regex)

            #define ASSERT_DEBUG_DEATH(statement, regex) \
              ASSERT_DEATH(statement, regex)

            #endif  // NDEBUG for EXPECT_DEBUG_DEATH

            可以看到,在Debug版和Release版本下, *_DEBUG_DEATH的定義不一樣。因為很多異常只會在Debug版本下拋出,而在Realease版本下不會拋出,所以針對Debug和Release分別做了不同的處理。看gtest里自帶的例子就明白了:

            int DieInDebugElse12(int* sideeffect) {
                
            if (sideeffect) *sideeffect = 12;
            #ifndef NDEBUG
                GTEST_LOG_(FATAL, 
            "debug death inside DieInDebugElse12()");
            #endif  // NDEBUG
                
            return 12;
            }

            TEST(TestCase, TestDieOr12WorksInDgbAndOpt)
            {
                
            int sideeffect = 0;
                
            // Only asserts in dbg.
                EXPECT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death");

                #ifdef NDEBUG
                
            // opt-mode has sideeffect visible.
                EXPECT_EQ(12, sideeffect);
                
            #else
                
            // dbg-mode no visible sideeffect.
                EXPECT_EQ(0, sideeffect);
                
            #endif
            }

            關(guān)于正則表達(dá)式

            POSIX系統(tǒng)(Linux, Cygwin, 和 Mac中,gtest的死亡測試中使用的是POSIX風(fēng)格的正則表達(dá)式,想了解POSIX風(fēng)格表達(dá)式可參考:

            1. POSIX extended regular expression

            2. Wikipedia entry.

            在Windows系統(tǒng)中,gtest的死亡測試中使用的是gtest自己實現(xiàn)的簡單的正則表達(dá)式語法。 相比POSIX風(fēng)格,gtest的簡單正則表達(dá)式少了很多內(nèi)容,比如 ("x|y"), ("(xy)"), ("[xy]") 和("x{5,7}")都不支持。

            下面是簡單正則表達(dá)式支持的一些內(nèi)容:


            matches any literal character c
            \\d matches any decimal digit
            \\D matches any character that's not a decimal digit
            \\f matches \f
            \\n matches \n
            \\r matches \r
            \\s matches any ASCII whitespace, including \n
            \\S matches any character that's not a whitespace
            \\t matches \t
            \\v matches \v
            \\w matches any letter, _, or decimal digit
            \\W matches any character that \\w doesn't match
            \\c matches any literal character c, which must be a punctuation
            . matches any single character except \n
            A? matches 0 or 1 occurrences of A
            A* matches 0 or many occurrences of A
            A+ matches 1 or many occurrences of A
            ^ matches the beginning of a string (not that of each line)
            $ matches the end of a string (not that of each line)
            xy matches x followed by y

            gtest定義兩個宏,用來表示當(dāng)前系統(tǒng)支持哪套正則表達(dá)式風(fēng)格:

            1. POSIX風(fēng)格:GTEST_USES_POSIX_RE = 1

            2. Simple風(fēng)格:GTEST_USES_SIMPLE_RE=1

            死亡測試運行方式

            1. fast方式(默認(rèn)的方式)

            testing::FLAGS_gtest_death_test_style = "fast";

            2. threadsafe方式

            testing::FLAGS_gtest_death_test_style = "threadsafe";


            你可以在 main() 里為所有的死亡測試設(shè)置測試形式,也可以為某次測試單獨設(shè)置。Google Test會在每次測試之前保存這個標(biāo)記并在測試完成后恢復(fù),所以你不需要去管這部分工作 。如:

            TEST(MyDeathTest, TestOne) {
              testing::FLAGS_gtest_death_test_style 
            = "threadsafe";
              
            // This test is run in the "threadsafe" style:
              ASSERT_DEATH(ThisShouldDie(), "");
            }

            TEST(MyDeathTest, TestTwo) {
              
            // This test is run in the "fast" style:
              ASSERT_DEATH(ThisShouldDie(), "");
            }

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

            注意事項

            1. 不要在死亡測試?yán)镝尫艃?nèi)存。

            2. 在父進(jìn)程里再次釋放內(nèi)存。

            3. 不要在程序中使用內(nèi)存堆檢查。

            六、運行參數(shù)

            使用gtest編寫的測試案例通常本身就是一個可執(zhí)行文件,因此運行起來非常方便。同時,gtest也為我們提供了一系列的運行參數(shù)(環(huán)境變量、命令行參數(shù)或代碼里指定),使得我們可以對案例的執(zhí)行進(jìn)行一些有效的控制。

            基本介紹

            前面提到,對于運行參數(shù),gtest提供了三種設(shè)置的途徑:

            1. 系統(tǒng)環(huán)境變量

            2. 命令行參數(shù)

            3. 代碼中指定FLAG

            因為提供了三種途徑,就會有優(yōu)先級的問題, 有一個原則是,最后設(shè)置的那個會生效。不過總結(jié)一下,通常情況下,比較理想的優(yōu)先級為:

            命令行參數(shù) > 代碼中指定FLAG > 系統(tǒng)環(huán)境變量

            為什么我們編寫的測試案例能夠處理這些命令行參數(shù)呢?是因為我們在main函數(shù)中,將命令行參數(shù)交給了gtest,由gtest來搞定命令行參數(shù)的問題。

            int _tmain(int argc, _TCHAR* argv[])
            {
                testing::InitGoogleTest(
            &argc, argv);
                
            return RUN_ALL_TESTS();
            }

            這樣,我們就擁有了接收和響應(yīng)gtest命令行參數(shù)的能力。如果需要在代碼中指定FLAG,可以使用testing::GTEST_FLAG這個宏來設(shè)置。比如相對于命令行參數(shù)--gtest_output,可以使用testing::GTEST_FLAG(output) = "xml:";來設(shè)置。注意到了,不需要加--gtest前綴了。同時,推薦將這句放置InitGoogleTest之前,這樣就可以使得對于同樣的參數(shù),命令行參數(shù)優(yōu)先級高于代碼中指定。

            int _tmain(int argc, _TCHAR* argv[])
            {
                testing::GTEST_FLAG(output) 
            = "xml:";
                testing::InitGoogleTest(
            &argc, argv);
                
            return RUN_ALL_TESTS();
            }

            最后再來說下第一種設(shè)置方式-系統(tǒng)環(huán)境變量。如果需要gtest的設(shè)置系統(tǒng)環(huán)境變量,必須注意的是:

            1. 系統(tǒng)環(huán)境變量全大寫,比如對于--gtest_output,響應(yīng)的系統(tǒng)環(huán)境變量為:GTEST_OUTPUT

            2.  有一個命令行參數(shù)例外,那就是--gtest_list_tests,它是不接受系統(tǒng)環(huán)境變量的。(只是用來羅列測試案例名稱)

            參數(shù)列表

            了解了上面的內(nèi)容,我這里就直接將所有命令行參數(shù)總結(jié)和羅列一下。如果想要獲得詳細(xì)的命令行說明,直接運行你的案例,輸入命令行參數(shù):/? 或 --help 或 -help

            1. 測試案例集合

            命令行參數(shù) 說明
            --gtest_list_tests 使用這個參數(shù)時,將不會執(zhí)行里面的測試案例,而是輸出一個案例的列表。
            --gtest_filter

            對執(zhí)行的測試案例進(jìn)行過濾,支持通配符

            ?    單個字符

            *    任意字符

            -    排除,如,-a 表示除了a

            :    取或,如,a:b 表示a或b

            比如下面的例子:

            ./foo_test 沒有指定過濾條件,運行所有案例
            ./foo_test --gtest_filter=* 使用通配符*,表示運行所有案例
            ./foo_test --gtest_filter=FooTest.* 運行所有“測試案例名稱(testcase_name)”為FooTest的案例
            ./foo_test --gtest_filter=*Null*:*Constructor* 運行所有“測試案例名稱(testcase_name)”或“測試名稱(test_name)”包含Null或Constructor的案例。
            ./foo_test --gtest_filter=-*DeathTest.* 運行所有非死亡測試案例。
            ./foo_test --gtest_filter=FooTest.*-FooTest.Bar 運行所有“測試案例名稱(testcase_name)”為FooTest的案例,但是除了FooTest.Bar這個案例

            --gtest_also_run_disabled_tests

            執(zhí)行案例時,同時也執(zhí)行被置為無效的測試案例。關(guān)于設(shè)置測試案例無效的方法為:

            在測試案例名稱或測試名稱中添加DISABLED前綴,比如:

            // Tests that Foo does Abc.
            TEST(FooTest, DISABLED_DoesAbc) {  }

            class DISABLED_BarTest : public testing::Test {  };

            // Tests that Bar does Xyz.
            TEST_F(DISABLED_BarTest, DoesXyz) {  }
            --gtest_repeat=[COUNT]

            設(shè)置案例重復(fù)運行次數(shù),非常棒的功能!比如:

            --gtest_repeat=1000      重復(fù)執(zhí)行1000次,即使中途出現(xiàn)錯誤。
            --gtest_repeat=-1          無限次數(shù)執(zhí)行。。。。
            --gtest_repeat=1000 --gtest_break_on_failure     重復(fù)執(zhí)行1000次,并且在第一個錯誤發(fā)生時立即停止。這個功能對調(diào)試非常有用。
            --gtest_repeat=1000 --gtest_filter=FooBar     重復(fù)執(zhí)行1000次測試案例名稱為FooBar的案例。

            測試案例輸出
            命令行參數(shù) 說明
            --gtest_color=(yes|no|auto) 輸出命令行時是否使用一些五顏六色的顏色。默認(rèn)是auto。
            --gtest_print_time 輸出命令行時是否打印每個測試案例的執(zhí)行時間。默認(rèn)是不打印的。
            --gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH]

            將測試結(jié)果輸出到一個xml中。

            1.--gtest_output=xml:    不指定輸出路徑時,默認(rèn)為案例當(dāng)前路徑。

            2.--gtest_output=xml:d:\ 指定輸出到某個目錄

            3.--gtest_output=xml:d:\foo.xml 指定輸出到d:\foo.xml

            如果不是指定了特定的文件路徑,gtest每次輸出的報告不會覆蓋,而會以數(shù)字后綴的方式創(chuàng)建。xml的輸出內(nèi)容后面介紹吧。

            對案例的異常處理
            命令行參數(shù) 說明
            --gtest_break_on_failure 調(diào)試模式下,當(dāng)案例失敗時停止,方便調(diào)試
            --gtest_throw_on_failure 當(dāng)案例失敗時以C++異常的方式拋出
            --gtest_catch_exceptions

            是否捕捉異常。gtest默認(rèn)是不捕捉異常的,因此假如你的測試案例拋了一個異常,很可能會彈出一個對話框,這非常的不友好,同時也阻礙了測試案例的運行。如果想不彈這個框,可以通過設(shè)置這個參數(shù)來實現(xiàn)。如將--gtest_catch_exceptions設(shè)置為一個非零的數(shù)。

            注意:這個參數(shù)只在Windows下有效。

            XML報告輸出格式

            <?xml version="1.0" encoding="UTF-8"?>
            <testsuites tests="3" failures="1" errors="0" time="35" name="AllTests">
              
            <testsuite name="MathTest" tests="2" failures="1"* errors="0" time="15">
                
            <testcase name="Addition" status="run" time="7" classname="">
                  
            <failure message="Value of: add(1, 1)  Actual: 3 Expected: 2" type=""/>
                  
            <failure message="Value of: add(1, -1)  Actual: 1 Expected: 0" type=""/>
                
            </testcase>
                
            <testcase name="Subtraction" status="run" time="5" classname="">
                
            </testcase>
              
            </testsuite>
              
            <testsuite name="LogicTest" tests="1" failures="0" errors="0" time="5">
                
            <testcase name="NonContradiction" status="run" time="5" classname="">
                
            </testcase>
              
            </testsuite>
            </testsuites>

            從報告里可以看出,我們之前在TEST等宏中定義的測試案例名稱(testcase_name)在xml測試報告中其實是一個testsuite name,而宏中的測試名稱(test_name)在xml測試報告中是一個testcase name,概念上似乎有點混淆,就看你怎么看吧。

            當(dāng)檢查點通過時,不會輸出任何檢查點的信息。當(dāng)檢查點失敗時,會有詳細(xì)的失敗信息輸出來failure節(jié)點。

            在我使用過程中發(fā)現(xiàn)一個問題,當(dāng)我同時設(shè)置了--gtest_filter參數(shù)時,輸出的xml報告中還是會包含所有測試案例的信息,只不過那些不被執(zhí)行的測試案例的status值為“notrun”。而我之前認(rèn)為的輸出的xml報告應(yīng)該只包含我需要運行的測試案例的信息。不知是否可提供一個只輸出需要執(zhí)行的測試案例的xml報告。因為當(dāng)我需要在1000個案例中執(zhí)行其中1個案例時,在報告中很難找到我運行的那個案例,雖然可以查找,但還是很麻煩。

            總結(jié)

            本篇主要介紹了gtest案例執(zhí)行時提供的一些參數(shù)的使用方法,這些參數(shù)都非常有用。在實際編寫gtest測試案例時肯定會需要用到的時候。至少我現(xiàn)在比較常用的就是:

            1. --gtest_filter

            2. --gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH]

            3. --gtest_catch_exceptions

            最后再總結(jié)一下我使用過程中遇到的幾個問題:

            1. 同時使用--gtest_filter和--gtest_output=xml:時,在xml測試報告中能否只包含過濾后的測試案例的信息。

            2. 有時,我在代碼中設(shè)置 testing::GTEST_FLAG(catch_exceptions) = 1和我在命令行中使用--gtest_catch_exceptions結(jié)果稍有不同,在代碼中設(shè)置FLAG方式有時候捕捉不了某些異常,但是通過命令行參數(shù)的方式一般都不會有問題。這是我曾經(jīng)遇到過的一個問題,最后我的處理辦法是既在代碼中設(shè)置FLAG,又在命令行參數(shù)中傳入--gtest_catch_exceptions。不知道是gtest在catch_exceptions方面不夠穩(wěn)定,還是我自己測試案例的問題。


            posted on 2009-06-02 11:19 Randy 閱讀(2346) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            <2009年6月>
            31123456
            78910111213
            14151617181920
            21222324252627
            2829301234
            567891011

            導(dǎo)航

            統(tǒng)計

            常用鏈接

            留言簿(3)

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            99久久成人国产精品免费| 久久久噜噜噜久久| 久久久久国产精品嫩草影院| 亚洲香蕉网久久综合影视| 国产L精品国产亚洲区久久| 成人久久免费网站| 久久伊人五月天论坛| 久久99国产精品久久99果冻传媒 | 亚洲级αV无码毛片久久精品| 久久亚洲国产中v天仙www| 国产色综合久久无码有码| 久久国产视屏| 日本精品久久久中文字幕| 色婷婷综合久久久久中文| 国产精品成人久久久| 国内精品久久久久久麻豆| 久久午夜伦鲁片免费无码| 久久久久精品国产亚洲AV无码| 77777亚洲午夜久久多喷| 国产精品内射久久久久欢欢| 久久精品无码午夜福利理论片| 久久伊人五月丁香狠狠色| 欧美久久久久久午夜精品| 国产午夜福利精品久久| 香蕉久久一区二区不卡无毒影院| 无码AV波多野结衣久久| 7777久久久国产精品消防器材| 国产精品亚洲综合久久| 亚洲欧美国产日韩综合久久| 久久国产精品无码网站| 精品无码久久久久久久动漫| 国产69精品久久久久99| 国内精品免费久久影院| 久久国产热这里只有精品| 久久香蕉国产线看观看猫咪?v| 国产日韩久久久精品影院首页| 国产A级毛片久久久精品毛片| 国内精品久久久久影院网站| 久久精品无码一区二区三区免费 | 亚洲欧美成人综合久久久| 久久精品aⅴ无码中文字字幕不卡 久久精品成人欧美大片 |