zoukankan      html  css  js  c++  java
  • 玩转Google开源C++单元测试框架Google Test系列(gtest)之八

    转载来源:https://www.cnblogs.com/coderzh/archive/2009/04/12/1434155.html

    一、前言

    上一篇我们分析了gtest的一些内部实现,总的来说整体的流程并不复杂。本篇我们就尝试编写一个精简版本的C++单元测试框架:nancytest ,通过编写这个简单的测试框架,将有助于我们理解gtest。

    二、整体设计

    使用最精简的设计,我们就用两个类,够简单吧:

    1. TestCase类
    包含单个测试案例的信息。

    2. UnitTest类

    负责所有测试案例的执行,管理。

    三、TestCase类

    TestCase类包含一个测试案例的基本信息,包括:测试案例名称,测试案例执行结果,同时还提供了测试案例执行的方法。我们编写的测试案例都继承自TestCase类。

    class TestCase
    {
    public:
        TestCase(const char* case_name) : testcase_name(case_name){}
    
        // 执行测试案例的方法
        virtual void Run() = 0;
    
        int nTestResult; // 测试案例的执行结果 
        const char* testcase_name; // 测试案例名称
    };

    四、UnitTest类

    我们的UnitTest类和gtest的一样,是一个单件。我们的UnitTest类的逻辑非常简单:

    1. 整个进程空间保存一个UnitTest 的单例。

    2. 通过RegisterTestCase()将测试案例添加到测试案例集合testcases_中。

    3. 执行测试案例时,调用UnitTest::Run(),遍历测试案例集合testcases_,调用案例的Run()方法

    class UnitTest
    {
    public:
        // 获取单例
        static UnitTest* GetInstance(); 
    
        // 注册测试案例
        TestCase* RegisterTestCase(TestCase* testcase);
        
        // 执行单元测试
        int Run();
    
        TestCase* CurrentTestCase; // 记录当前执行的测试案例
        int nTestResult; // 总的执行结果
        int nPassed; // 通过案例数
        int nFailed; // 失败案例数
    protected:
        std::vector<TestCase*> testcases_; // 案例集合
    };

    下面是UnitTest类的实现:

    UnitTest* UnitTest::GetInstance()
    {
        static UnitTest instance;
        return &instance;
    }
    
    TestCase* UnitTest::RegisterTestCase(TestCase* testcase)
    {
        testcases_.push_back(testcase);
        return testcase;
    }
    
    int UnitTest::Run()
    {
        nTestResult = 1;
        for (std::vector<TestCase*>::iterator it = testcases_.begin();
            it != testcases_.end(); ++it)
        {
            TestCase* testcase = *it;
            CurrentTestCase = testcase;
            std::cout << green << "======================================" << std::endl;
            std::cout << green << "Run TestCase:" << testcase->testcase_name << std::endl;
            testcase->Run();
            std::cout << green << "End TestCase:" << testcase->testcase_name << std::endl;
            if (testcase->nTestResult)
            {
                nPassed++;
            }
            else
            {
                nFailed++;
                nTestResult = 0;
            }
        }
    
        std::cout << green << "======================================" << std::endl;
        std::cout << green << "Total TestCase : " << nPassed + nFailed << std::endl;
        std::cout << green << "Passed : " << nPassed << std::endl;
        std::cout << red << "Failed : " << nFailed << std::endl;
        return nTestResult;
    }

    五、NTEST宏

    接下来定一个宏NTEST,方便我们写我们的测试案例的类。

    #define TESTCASE_NAME(testcase_name) 
        testcase_name##_TEST        // ##用法,拼接作用
    
    #define NANCY_TEST_(testcase_name) 
    class TESTCASE_NAME(testcase_name) : public TestCase 
    { 
    public: 
        TESTCASE_NAME(testcase_name)(const char* case_name) : TestCase(case_name){}; 
        virtual void Run(); 
    private: 
        static TestCase* const testcase_; 
    }; 
    
    TestCase* const TESTCASE_NAME(testcase_name) 
        ::testcase_ = UnitTest::GetInstance()->RegisterTestCase( 
            new TESTCASE_NAME(testcase_name)(#testcase_name));   //#的用法,将宏定义传入参数转为const char*
    void TESTCASE_NAME(testcase_name)::Run()//run时候就会去执行NTEST宏定义的body
    
    #define NTEST(testcase_name) 
        NANCY_TEST_(testcase_name)

    六、RUN_ALL_TEST宏

    然后是执行所有测试案例的一个宏:

    #define RUN_ALL_TESTS() 
        UnitTest::GetInstance()->Run();

    七、断言的宏EXPECT_EQ

    这里,我只写一个简单的EXPECT_EQ :

    #define EXPECT_EQ(m, n) 
        if (m != n) 
        { 
            UnitTest::GetInstance()->CurrentTestCase->nTestResult = 0; 
            std::cout << red << "Failed" << std::endl; 
            std::cout << red << "Expect:" << m << std::endl; 
            std::cout << red << "Actual:" << n << std::endl; 
        }

    八、案例Demo

    够简单吧,再来看看案例怎么写:

    #include "nancytest.h"
    
    int Foo(int a, int b)
    {
        return a + b;
    }
    
    NTEST(FooTest_PassDemo)//运行程序时先加载这两个宏定义,但不执行内容,相当于实例化了两个子类(继承TestCase)
    {
        EXPECT_EQ(3, Foo(1, 2));
        EXPECT_EQ(2, Foo(1, 1));
    }
    
    NTEST(FooTest_FailDemo)
    {
        EXPECT_EQ(4, Foo(1, 2));
        EXPECT_EQ(2, Foo(1, 2));
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        return RUN_ALL_TESTS();
    }

    测试结果

     总结:

    1.工厂方法,基类指针指向派生类对象,实现(纯)虚函数功能

    2.活用宏定义

  • 相关阅读:
    每天一道LeetCode--141.Linked List Cycle(链表环问题)
    每天一道LeetCode--119.Pascal's Triangle II(杨辉三角)
    每天一道LeetCode--118. Pascal's Triangle(杨辉三角)
    CF1277D Let's Play the Words?
    CF1281B Azamon Web Services
    CF1197D Yet Another Subarray Problem
    CF1237D Balanced Playlist
    CF1239A Ivan the Fool and the Probability Theory
    CF1223D Sequence Sorting
    CF1228D Complete Tripartite
  • 原文地址:https://www.cnblogs.com/YangARTuan/p/13737507.html
Copyright © 2011-2022 走看看