国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C++ > 正文

Google開(kāi)源C++單元測(cè)試框架gTest 7:深入探索gTest

2019-11-07 23:19:07
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

一、前言

“深入解析”對(duì)我來(lái)說(shuō)的確有些難度,所以我盡量將我學(xué)習(xí)到和觀察到的gtest內(nèi)部實(shí)現(xiàn)介紹給大家。本文算是拋磚引玉吧,只能是對(duì)gtest的整體結(jié)構(gòu)的一些介紹,想要了解更多細(xì)節(jié)最好的辦法還是看gtest源碼,如果你看過(guò)gtest源碼,你會(huì)發(fā)現(xiàn)里面的注釋非常的詳細(xì)!好了,下面就開(kāi)始了解gtest吧。

二、從TEST宏開(kāi)始

前面的文章已經(jīng)介紹過(guò)TEST宏的用法了,通過(guò)TEST宏,我們可以非法簡(jiǎn)單、方便的編寫(xiě)測(cè)試案例,比如:

TEST(FooTest, Demo){    EXPECT_EQ(1, 1);}

 

我們先不去看TEST宏的定義,而是先使用/P參數(shù)將TEST展開(kāi)。如果使用的是Vistual Studio的話:

1. 選中需要展開(kāi)的代碼文件,右鍵 - 屬性 - C/C++ - PReprocessor

2. Generate Preprocessed File 設(shè)置 Without Line Numbers (/EP /P) 或 With Line Numbers (/P)

3. 關(guān)閉屬性對(duì)話框,右鍵選中需要展開(kāi)的文件,右鍵菜單中點(diǎn)擊:Compile

編譯過(guò)后,會(huì)在源代碼目錄生成一個(gè)后綴為.i的文件,比如我對(duì)上面的代碼進(jìn)行展開(kāi),展開(kāi)后的內(nèi)容為:

復(fù)制代碼class FooTest_Demo_Test : public ::testing::Test {public:     FooTest_Demo_Test() {}private:     virtual void TestBody();    static ::testing::TestInfo* const test_info_;    FooTest_Demo_Test(const FooTest_Demo_Test &);    void Operator=(const FooTest_Demo_Test &);};::testing::TestInfo* const FooTest_Demo_Test     ::test_info_ =         ::testing::internal::MakeAndRegisterTestInfo(             "FooTest", "Demo", "", "",            (::testing::internal::GetTestTypeId()),            ::testing::Test::SetUpTestCase,            ::testing::Test::TearDownTestCase,            new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>);void FooTest_Demo_Test::TestBody(){    switch (0)    case 0:        if (const ::testing::AssertionResult                 gtest_ar =                     (::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(1)) == 1)>::Compare("1", "1", 1, 1)))            ;        else             ::testing::internal::AssertHelper(                ::testing::TPRT_NONFATAL_FAILURE,                ".//gtest_demo.cpp",                9,                gtest_ar.failure_message()                ) = ::testing::Message();}復(fù)制代碼

 

展開(kāi)后,我們觀察到:

1. TEST宏展開(kāi)后,是一個(gè)繼承自testing::Test的類(lèi)。

2. 我們?cè)赥EST宏里面寫(xiě)的測(cè)試代碼,其實(shí)是被放到了類(lèi)的TestBody方法中。

3. 通過(guò)靜態(tài)變量test_info_,調(diào)用MakeAndRegisterTestInfo對(duì)測(cè)試案例進(jìn)行注冊(cè)。

如下圖:

上面關(guān)鍵的方法就是MakeAndRegisterTestInfo了,我們跳到MakeAndRegisterTestInfo函數(shù)中:

復(fù)制代碼// 創(chuàng)建一個(gè) TestInfo 對(duì)象并注冊(cè)到 Google Test;// 返回創(chuàng)建的TestInfo對(duì)象//// 參數(shù):////   test_case_name:            測(cè)試案例的名稱(chēng)//   name:                           測(cè)試的名稱(chēng)//   test_case_comment:       測(cè)試案例的注釋信息//   comment:                      測(cè)試的注釋信息//   fixture_class_id:             test fixture類(lèi)的ID//   set_up_tc:                    事件函數(shù)SetUpTestCases的函數(shù)地址//   tear_down_tc:               事件函數(shù)TearDownTestCases的函數(shù)地址//   factory:                        工廠對(duì)象,用于創(chuàng)建測(cè)試對(duì)象(Test)TestInfo* MakeAndRegisterTestInfo(    const char* test_case_name, const char* name,    const char* test_case_comment, const char* comment,    TypeId fixture_class_id,    SetUpTestCaseFunc set_up_tc,    TearDownTestCaseFunc tear_down_tc,    TestFactoryBase* factory) {  TestInfo* const test_info =      new TestInfo(test_case_name, name, test_case_comment, comment,                   fixture_class_id, factory);  GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);  return test_info;}復(fù)制代碼

 

我們看到,上面創(chuàng)建了一個(gè)TestInfo對(duì)象,然后通過(guò)AddTestInfo注冊(cè)了這個(gè)對(duì)象。TestInfo對(duì)象到底是一個(gè)什么樣的東西呢?

TestInfo對(duì)象主要用于包含如下信息:

1. 測(cè)試案例名稱(chēng)(testcase name)

2. 測(cè)試名稱(chēng)(test name)

3. 該案例是否需要執(zhí)行

4. 執(zhí)行案例時(shí),用于創(chuàng)建Test對(duì)象的函數(shù)指針

5. 測(cè)試結(jié)果 

我們還看到,TestInfo的構(gòu)造函數(shù)中,非常重要的一個(gè)參數(shù)就是工廠對(duì)象,它主要負(fù)責(zé)在運(yùn)行測(cè)試案例時(shí)創(chuàng)建出Test對(duì)象。我們看到我們上面的例子的factory為:

new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>

 

我們明白了,Test對(duì)象原來(lái)就是TEST宏展開(kāi)后的那個(gè)類(lèi)的對(duì)象(FooTest_Demo_Test),再看看TestFactoryImpl的實(shí)現(xiàn):

template <class TestClass>class TestFactoryImpl : public TestFactoryBase {public:    virtual Test* CreateTest() { return new TestClass; }};

 

這個(gè)對(duì)象工廠夠簡(jiǎn)單吧,嗯,Simple is better。當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)測(cè)試對(duì)象(Test)時(shí),調(diào)用factory的CreateTest()方法就可以了。 

創(chuàng)建了TestInfo對(duì)象后,再通過(guò)下面的方法對(duì)TestInfo對(duì)象進(jìn)行注冊(cè):

GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

 

GetUnitTestImpl()是獲取UnitTestImpl對(duì)象:

inline UnitTestImpl* GetUnitTestImpl() {    return UnitTest::GetInstance()->impl();}

 

其中UnitTest是一個(gè)單件(Singleton),整個(gè)進(jìn)程空間只有一個(gè)實(shí)例,通過(guò)UnitTest::GetInstance()獲取單件的實(shí)例。上面的代碼看到,UnitTestImpl對(duì)象是最終是從UnitTest對(duì)象中獲取的。那么UnitTestImpl到底是一個(gè)什么樣的東西呢?可以這樣理解:

UnitTestImpl是一個(gè)在UnitTest內(nèi)部使用的,為執(zhí)行單元測(cè)試案例而提供了一系列實(shí)現(xiàn)的那么一個(gè)類(lèi)。(自己歸納的,可能不準(zhǔn)確)

我們上面的AddTestInfo就是其中的一個(gè)實(shí)現(xiàn),負(fù)責(zé)注冊(cè)TestInfo實(shí)例:

 

復(fù)制代碼// 添加TestInfo對(duì)象到整個(gè)單元測(cè)試中//// 參數(shù):////   set_up_tc:      事件函數(shù)SetUpTestCases的函數(shù)地址//   tear_down_tc: 事件函數(shù)TearDownTestCases的函數(shù)地址//   test_info:        TestInfo對(duì)象void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,               Test::TearDownTestCaseFunc tear_down_tc,               TestInfo * test_info) {// 處理死亡測(cè)試的代碼,先不關(guān)注它if (original_working_dir_.IsEmpty()) {    original_working_dir_.Set(FilePath::GetCurrentDir());    if (original_working_dir_.IsEmpty()) {        printf("%s/n", "Failed to get the current working directory.");        abort();    }}// 獲取或創(chuàng)建了一個(gè)TestCase對(duì)象,并將testinfo添加到TestCase對(duì)象中。GetTestCase(test_info->test_case_name(),            test_info->test_case_comment(),            set_up_tc,            tear_down_tc)->AddTestInfo(test_info);}復(fù)制代碼

我們看到,TestCase對(duì)象出來(lái)了,并通過(guò)AddTestInfo添加了一個(gè)TestInfo對(duì)象。這時(shí),似乎豁然開(kāi)朗了:

1. TEST宏中的兩個(gè)參數(shù),第一個(gè)參數(shù)testcase_name,就是TestCase對(duì)象的名稱(chēng),第二個(gè)參數(shù)test_name就是Test對(duì)象的名稱(chēng)。而TestInfo包含了一個(gè)測(cè)試案例的一系列信息。

2. 一個(gè)TestCase對(duì)象對(duì)應(yīng)一個(gè)或多個(gè)TestInfo對(duì)象。

 

我們來(lái)看看TestCase的創(chuàng)建過(guò)程(UnitTestImpl::GetTestCase):

復(fù)制代碼// 查找并返回一個(gè)指定名稱(chēng)的TestCase對(duì)象。如果對(duì)象不存在,則創(chuàng)建一個(gè)并返回//// 參數(shù):////   test_case_name:    測(cè)試案例名稱(chēng)//   set_up_tc:            事件函數(shù)SetUpTestCases的函數(shù)地址//   tear_down_tc:       事件函數(shù)TearDownTestCases的函數(shù)地址TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,                                    const char* comment,                                    Test::SetUpTestCaseFunc set_up_tc,                                    Test::TearDownTestCaseFunc tear_down_tc) {  // 從test_cases里查找指定名稱(chēng)的TestCase    internal::ListNode<TestCase*>* node = test_cases_.FindIf(        TestCaseNameIs(test_case_name));    if (node == NULL) {        // 沒(méi)找到,我們來(lái)創(chuàng)建一個(gè)        TestCase* const test_case =            new TestCase(test_case_name, comment, set_up_tc, tear_down_tc);        // 判斷是否為死亡測(cè)試案例        if (internal::UnitTestOptions::MatchesFilter(String(test_case_name),                                                 kDeathTestCaseFilter)) {            // 是的話,將該案例插入到最后一個(gè)死亡測(cè)試案例后            node = test_cases_.InsertAfter(last_death_test_case_, test_case);            last_death_test_case_ = node;        } else {            // 否則,添加到test_cases最后。            test_cases_.PushBack(test_case);            node = test_cases_.Last();        }    }    // 返回TestCase對(duì)象    return node->element();}復(fù)制代碼

 

三、回過(guò)頭看看TEST宏的定義

#define TEST(test_case_name, test_name)/    GTEST_TEST_(test_case_name, test_name, /              ::testing::Test, ::testing::internal::GetTestTypeId())

 

同時(shí)也看看TEST_F宏

#define TEST_F(test_fixture, test_name)/    GTEST_TEST_(test_fixture, test_name, test_fixture, /              ::testing::internal::GetTypeId<test_fixture>())

都是使用了GTEST_TEST_宏,在看看這個(gè)宏如何定義的:

復(fù)制代碼#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)/class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {/public:/    GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}/private:/    virtual void TestBody();/    static ::testing::TestInfo* const test_info_;/    GTEST_DISALLOW_COPY_AND_ASSIGN_(/        GTEST_TEST_CLASS_NAME_(test_case_name, test_name));/};//::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)/    ::test_info_ =/        ::testing::internal::MakeAndRegisterTestInfo(/            #test_case_name, #test_name, "", "", /            (parent_id), /            parent_class::SetUpTestCase, /            parent_class::TearDownTestCase, /            new ::testing::internal::TestFactoryImpl</                GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);/void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()復(fù)制代碼

 

不需要多解釋了,和我們上面展開(kāi)看到的差不多,不過(guò)這里比較明確的看到了,我們?cè)赥EST宏里寫(xiě)的就是TestBody里的東西。這里再補(bǔ)充說(shuō)明一下里面的GTEST_DISALLOW_COPY_AND_ASSIGN_宏,我們上面的例子看出,這個(gè)宏展開(kāi)后:

FooTest_Demo_Test(const FooTest_Demo_Test &);void operator=(const FooTest_Demo_Test &);

 

正如這個(gè)宏的名字一樣,它是用于防止對(duì)對(duì)象進(jìn)行拷貝和賦值操作的。 

四、再來(lái)了解RUN_ALL_TESTS宏

我們的測(cè)試案例的運(yùn)行就是通過(guò)這個(gè)宏發(fā)起的。RUN_ALL_TEST的定義非常簡(jiǎn)單:

#define RUN_ALL_TESTS()/    (::testing::UnitTest::GetInstance()->Run())

 

我們又看到了熟悉的::testing::UnitTest::GetInstance(),看來(lái)案例的執(zhí)行時(shí)從UnitTest的Run方法開(kāi)始的,我提取了一些Run中的關(guān)鍵代碼,如下:

復(fù)制代碼int UnitTest::Run() {    __try {        return impl_->RunAllTests();    } __except(internal::UnitTestOptions::GTestShouldProcessSEH(        GetExceptionCode())) {        printf("Exception thrown with code 0x%x./nFAIL/n", GetExceptionCode());        fflush(stdout);        return 1;    }    return impl_->RunAllTests();}復(fù)制代碼

 

我們又看到了熟悉的impl(UnitTestImpl),具體案例該怎么執(zhí)行,還是得靠UnitTestImpl。

復(fù)制代碼int UnitTestImpl::RunAllTests() {    // ...    printer->OnUnitTestStart(parent_);    // 計(jì)時(shí)    const TimeInMillis start = GetTimeInMillis();    printer->OnGlobalSetUpStart(parent_);    // 執(zhí)行全局的SetUp事件    environments_.ForEach(SetUpEnvironment);    printer->OnGlobalSetUpEnd(parent_);    // 全局的SetUp事件執(zhí)行成功的話    if (!Test::HasFatalFailure()) {        // 執(zhí)行每個(gè)測(cè)試案例        test_cases_.ForEach(TestCase::RunTestCase);    }    // 執(zhí)行全局的TearDown事件    printer->OnGlobalTearDownStart(parent_);    environments_in_reverse_order_.ForEach(TearDownEnvironment);    printer->OnGlobalTearDownEnd(parent_);    elapsed_time_ = GetTimeInMillis() - start;    // 執(zhí)行完成    printer->OnUnitTestEnd(parent_);    // Gets the result and clears it.    if (!Passed()) {      failed = true;    }    ClearResult();    // 返回測(cè)試結(jié)果    return failed ? 1 : 0;}復(fù)制代碼

 

上面,我們很開(kāi)心的看到了我們前面講到的全局事件的調(diào)用。environments_是一個(gè)Environment的鏈表結(jié)構(gòu)(List),它的內(nèi)容是我們?cè)趍ain中通過(guò):

testing::AddGlobalTestEnvironment(new FooEnvironment);

 

添加進(jìn)去的。test_cases_我們之前也了解過(guò)了,是一個(gè)TestCase的鏈表結(jié)構(gòu)(List)。gtest實(shí)現(xiàn)了一個(gè)鏈表,并且提供了一個(gè)Foreach方法,迭代調(diào)用某個(gè)函數(shù),并將里面的元素作為函數(shù)的參數(shù):

復(fù)制代碼template <typename F>  // F is the type of the function/functorvoid ForEach(F functor) const {    for ( const ListNode<E> * node = Head();          node != NULL;          node = node->next() ) {      functor(node->element());    }}復(fù)制代碼

 

因此,我們關(guān)注一下:environments_.ForEach(SetUpEnvironment),其實(shí)是迭代調(diào)用了SetUpEnvironment函數(shù):

static void SetUpEnvironment(Environment* env) { env->SetUp(); }

 

最終調(diào)用了我們定義的SetUp()函數(shù)。

再看看test_cases_.ForEach(TestCase::RunTestCase)的TestCase::RunTestCase實(shí)現(xiàn):

static void RunTestCase(TestCase * test_case) { test_case->Run(); }

 

再看TestCase的Run實(shí)現(xiàn):

復(fù)制代碼void TestCase::Run() {    if (!should_run_) return;    internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();    impl->set_current_test_case(this);    UnitTestEventListenerInterface * const result_printer =    impl->result_printer();    result_printer->OnTestCaseStart(this);    impl->os_stack_trace_getter()->UponLeavingGTest();    // 哈!SetUpTestCases事件在這里調(diào)用    set_up_tc_();    const internal::TimeInMillis start = internal::GetTimeInMillis();    // 嗯,前面分析的一個(gè)TestCase對(duì)應(yīng)多個(gè)TestInfo,因此,在這里迭代對(duì)TestInfo調(diào)用RunTest方法    test_info_list_->ForEach(internal::TestInfoImpl::RunTest);    elapsed_time_ = internal::GetTimeInMillis() - start;    impl->os_stack_trace_getter()->UponLeavingGTest();    // TearDownTestCases事件在這里調(diào)用    tear_down_tc_();    result_printer->OnTestCaseEnd(this);    impl->set_current_test_case(NULL);}復(fù)制代碼

第二種事件機(jī)制又浮出我們眼前,非常興奮??梢钥闯?,SetUpTestCases和TearDownTestCaess是在一個(gè)TestCase之前和之后調(diào)用的。接著看test_info_list_->ForEach(internal::TestInfoImpl::RunTest):

static void RunTest(TestInfo * test_info) {    test_info->impl()->Run();}

哦?TestInfo也有一個(gè)impl?看來(lái)我們之前漏掉了點(diǎn)東西,和UnitTest很類(lèi)似,TestInfo內(nèi)部也有一個(gè)主管各種實(shí)現(xiàn)的類(lèi),那就是TestInfoImpl,它在TestInfo的構(gòu)造函數(shù)中創(chuàng)建了出來(lái)(還記得前面講的TestInfo的創(chuàng)建過(guò)程嗎?):

復(fù)制代碼TestInfo::TestInfo(const char* test_case_name,                   const char* name,                   const char* test_case_comment,                   const char* comment,                   internal::TypeId fixture_class_id,                   internal::TestFactoryBase* factory) {    impl_ = new internal::TestInfoImpl(this, test_case_name, name,                                     test_case_comment, comment,                                     fixture_class_id, factory);}復(fù)制代碼

 

因此,案例的執(zhí)行還得看TestInfoImpl的Run()方法,同樣,我簡(jiǎn)化一下,只列出關(guān)鍵部分的代碼:

復(fù)制代碼void TestInfoImpl::Run() {

    // ...

    UnitTestEventListenerInterface* const result_printer =        impl->result_printer();    result_printer->OnTestStart(parent_);    // 開(kāi)始計(jì)時(shí)    const TimeInMillis start = GetTimeInMillis();    Test* test = NULL;    __try {        // 我們的對(duì)象工廠,使用CreateTest()生成Test對(duì)象        test = factory_->CreateTest();    } __except(internal::UnitTestOptions::GTestShouldProcessSEH(        GetExceptionCode())) {        AddExceptionThrownFailure(GetExceptionCode(),                              "the test fixture's constructor");        return;

    }

    // 如果Test對(duì)象創(chuàng)建成功

    if (!Test::HasFatalFailure()) {

        // 調(diào)用Test對(duì)象的Run()方法,執(zhí)行測(cè)試案例 

        test->Run();    }    // 執(zhí)行完畢,刪除Test對(duì)象    impl->os_stack_trace_getter()->UponLeavingGTest();    delete test;    test = NULL;    // 停止計(jì)時(shí)    result_.set_elapsed_time(GetTimeInMillis() - start);    result_printer->OnTestEnd(parent_);}復(fù)制代碼

 

上面看到了我們前面講到的對(duì)象工廠fatory,通過(guò)fatory的CreateTest()方法,創(chuàng)建Test對(duì)象,然后執(zhí)行案例又是通過(guò)Test對(duì)象的Run()方法:

復(fù)制代碼void Test::Run() {    if (!HasSameFixtureClass()) return;    internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();    impl->os_stack_trace_getter()->UponLeavingGTest();    __try {        // Yeah!每個(gè)案例的SetUp事件在這里調(diào)用        SetUp();    } __except(internal::UnitTestOptions::GTestShouldProcessSEH(        GetExceptionCode())) {        AddExceptionThrownFailure(GetExceptionCode(), "SetUp()");    }    // We will run the test only if SetUp() had no fatal failure.    if (!HasFatalFailure()) {        impl->os_stack_trace_getter()->UponLeavingGTest();        __try {            // 哈哈??!千辛萬(wàn)苦,我們定義在TEST宏里的東西終于被調(diào)用了!            TestBody();        } __except(internal::UnitTestOptions::GTestShouldProcessSEH(            GetExceptionCode())) {            AddExceptionThrownFailure(GetExceptionCode(), "the test body");        }    }    impl->os_stack_trace_getter()->UponLeavingGTest();    __try {        // 每個(gè)案例的TearDown事件在這里調(diào)用        TearDown();    } __except(internal::UnitTestOptions::GTestShouldProcessSEH(        GetExceptionCode())) {        AddExceptionThrownFailure(GetExceptionCode(), "TearDown()");    }}復(fù)制代碼

 

上面的代碼里非常極其以及特別的興奮的看到了執(zhí)行測(cè)試案例的前后事件,測(cè)試案例執(zhí)行TestBody()的代碼。仿佛整個(gè)gtest的流程在眼前一目了然了。

四、總結(jié)

本文通過(guò)分析TEST宏和RUN_ALL_TEST宏,了解到了整個(gè)gtest運(yùn)作過(guò)程,可以說(shuō)整個(gè)過(guò)程簡(jiǎn)潔而優(yōu)美。之前讀《代碼之美》,感觸頗深,現(xiàn)在讀過(guò)gtest代碼,再次讓我感觸深刻。記得很早前,我對(duì)設(shè)計(jì)的理解是“功能越強(qiáng)大越好,設(shè)計(jì)越復(fù)雜越好,那樣才顯得?!?,漸漸得,我才發(fā)現(xiàn),簡(jiǎn)單才是最好。我曾總結(jié)過(guò)自己寫(xiě)代碼的設(shè)計(jì)原則:功能明確,設(shè)計(jì)簡(jiǎn)單。了解了gtest代碼后,猛然發(fā)現(xiàn)gtest不就是這樣嗎,同時(shí)gtest也給了我很多驚喜,因此,我對(duì)gtest的評(píng)價(jià)是:功能強(qiáng)大,設(shè)計(jì)簡(jiǎn)單,使用方便。

總結(jié)一下gtest里的幾個(gè)關(guān)鍵的對(duì)象:

1. UnitTest 單例,總管整個(gè)測(cè)試,包括測(cè)試環(huán)境信息,當(dāng)前執(zhí)行狀態(tài)等等。

2. UnitTestImpl UnitTest內(nèi)部具體功能的實(shí)現(xiàn)者。

3. Test    我們自己編寫(xiě)的,或通過(guò)TEST,TEST_F等宏展開(kāi)后的Test對(duì)象,管理著測(cè)試案例的前后事件,具體的執(zhí)行代碼TestBody。

4. TestCase 測(cè)試案例對(duì)象,管理著基于TestCase的前后事件,管理內(nèi)部多個(gè)TestInfo。

5. TestInfo  管理著測(cè)試案例的基本信息,包括Test對(duì)象的創(chuàng)建方法。 

6. TestInfoImpl TestInfo內(nèi)部具體功能的實(shí)現(xiàn)者 。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 永靖县| 亳州市| 七台河市| 金门县| 若尔盖县| 汕尾市| 红原县| 安庆市| 阿拉善盟| 方城县| 收藏| 合肥市| 贵阳市| 双柏县| 青铜峡市| 门源| 哈巴河县| 桃园市| 祁连县| 南江县| 云南省| 博罗县| 莱州市| 金湖县| 岫岩| 旬邑县| 黄浦区| 丹阳市| 景洪市| 枞阳县| 烟台市| 夏津县| 阿勒泰市| 元氏县| 启东市| 石景山区| 阿拉善左旗| 临湘市| 瑞安市| 清涧县| 常熟市|