当前位置:   article > 正文

CppTest单元测试框架(更新)

CppTest单元测试框架(更新)

1 背景

前面文章单元测试之CppTest测试框架中讲述利用宏ADD_SUITE将测试用例自动增加到测试框架中。但在使用中发现一个问题,就是通过宏ADD_SUITE增加多个测试Suite时,每次运行时都是所有测试Suite都运行,有的Suite运行比较慢,这对边写测试用例边编译运行时效率很低。于是就在原来测试框架下作出修改,即默认运行所有测试用例,不过可以通过命令指定测试用例来运行。

2 设计

修改后新的类图如下:
类图

修改说明:

  • TestApp 增加成员suites_,
  • addSuite增加参数name,表示测试Suite名字,该函数实现将suite增加到成员suites_中存起来。
  • run接口没变,实现时从suites_将suite增加到mainSuite_中,如果没指定测试用例则全部增加,否则只增加指定测试用例。
  • AutoAddSuite的构造函数增加参数用例名称。
  • 宏ADD_SUITE参数没变化,实现时将类型作为测试用例名称来注册

类定义如下:

#ifndef TESTAPP_H
#define TESTAPP_H
#include <cpptest/cpptest.h>
#include <map>
#include <memory>

class TestApp
{
    typedef std::map<std::string, std::unique_ptr<Test::Suite>> Suites;
    Test::Suite mainSuite_;
    Suites suites_;
    TestApp();
public:
    static TestApp& Instance();

    void  addSuite(const char* name, Test::Suite * suite);
    int run(int argc, char *argv[]);
};

#define theTestApp TestApp::Instance()

template<typename Suite>
struct AutoAddSuite
{
    AutoAddSuite(const char* Name) { theTestApp.addSuite(Name, new Suite()); }
};

#define ADD_SUITE(Type) AutoAddSuite<Type>  add##Type(#Type);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

说明:

  • TestApp类型是单例类,提高增加Suite接口和run接口
  • AutoAddSuite是一个自动添加Suite的模板类型
  • 宏ADD_SUITE定义了AutoAddSuite对象,用于自动添加。

3 实现

#include "testapp.h"

#include <iostream>
#include <tuple>
#include <cstring>
#include <cstdio>

namespace
{
void usage()
{
    std::cout << "usage: test [MODE] [Suite]\n"
         << "where MODE may be one of:\n"
         << "  --compiler\n"
         << "  --html\n"
         << "  --text-terse (default)\n"
         << "  --text-verbose\n";
}

std::tuple<std::string, std::unique_ptr<Test::Output>>
cmdline(int argc, char* argv[])
{
    Test::Output* output = 0;
    std::string name;

    if (argc == 1)
        output = new Test::TextOutput(Test::TextOutput::Verbose);
    if(argc > 1)
    {
        const char* arg = argv[1];
        if (strcmp(arg, "--compiler") == 0)
            output = new Test::CompilerOutput;
        else if (strcmp(arg, "--html") == 0)
            output =  new Test::HtmlOutput;
        else if (strcmp(arg, "--text-terse") == 0)
            output = new Test::TextOutput(Test::TextOutput::Terse);
        else if (strcmp(arg, "--text-verbose") == 0)
            output = new Test::TextOutput(Test::TextOutput::Verbose);
        else if(strcmp(arg, "--help") == 0)
            std::tuple<std::string, std::unique_ptr<Test::Output>>("help", output);
        else
            std::cout << "invalid commandline argument: " << arg << std::endl;
    }
    if(argc > 2)
        name = argv[2];
    return std::tuple<std::string, std::unique_ptr<Test::Output>>(name, output);
}
}

TestApp & TestApp::Instance()
{
    static TestApp theApp;
    return theApp;
}

TestApp::TestApp()
{}

void TestApp::addSuite(const char* name, Test::Suite * suite)
{
    suites_.emplace(name, std::unique_ptr<Test::Suite>(suite));
}

int TestApp::run(int argc, char *argv[])
{
    try
    {
        auto params = cmdline(argc, argv);
        std::string name(std::move(std::get<0>(params)));
        std::unique_ptr<Test::Output> output(std::move(std::get<1>(params)));
        if(name == "help" || !output)
        {
            usage();
            std::cout << "where Suite may be one of(default - all):\n";
            for(auto & suite: suites_)
                std::cout << "  " << suite.first << "\n";
            return 0;
        }

        for(auto & suite: suites_)
        {
            if(name.empty())
                mainSuite_.add(std::move(suite.second));
            else if(name == suite.first)
            {
                mainSuite_.add(std::move(suite.second));
                break;
            }
        }
        mainSuite_.run(*output, true);

        Test::HtmlOutput* const html = dynamic_cast<Test::HtmlOutput*>(output.get());
        if (html)
            html->generate(std::cout, true, argv[0]);
    }
    catch (...)
    {
        std::cout << "unexpected exception encountered\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

说明:

  • Instance 返回一个单例引用
  • addSuite 增加Suite到suites_
  • run
    • 首先根据命令行返回Test::Output和要单独运行测试用例名称
    • 如果参数错误或help显示用法后退出主程序。
    • 遍历suites_,将suite添加到mainSuite_中(如果name不为空,则只添加名称为name的测试用例)
    • 然后调用mainSuite_运行测试用例
    • 最后如果类型是Output是Test::HtmlOutput类型,则将结果输出到标准输出std::cout.

4 使用

4.1 主函数

#include "testapp.h"

int main(int argc, char *argv[])
{
    try
    {
        theTestApp.run(argc, argv);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

主函数很简单,变化。

4.2 使用方法

这里假定程序名称concurrent,显示用法:

 $ ./concurrent --help
usage: test [MODE] [Suite]
where MODE may be one of:
  --compiler
  --html
  --text-terse (default)
  --text-verbose
where Suite may be one of(default - all):
  AtomicSuite
  BlockQueueSuite
  ConditionVariableSuite
  FutureSuite
  LocksSuite
  MutexSuite
  RingQueueSuite
  ThreadSuite
  TimedMutexSuite
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

运行测试用例BlockQueueSuite:

$ ./concurrent --text-terse BlockQueueSuite
BlockQueueSuite: 0/2
I get a Apple pie
I get a Banana pie
I get a Pear pie
I get a Plum pie
I get a Pineapple pie

I get a Apple pie
I get a Banana pie
I get a Pear pie
I get a Plum pie
I get a Pineapple pie

I get a Apple
I get a Banana
I get a Pear
I get a Plum
I get a Pineapple
BlockQueueSuite: 1/2
I get a Apple pie in thread(3)

I get a Banana pie in thread(4)

I get a Pear pie in thread(0)

I get a Plum pie in thread(2)

I get a Pineapple pie in thread(1)

I get a Apple pie in thread(0)

I get a Banana pie in thread(2)

I get a Pear pie in thread(3)

I get a Plum pie in thread(1)

I get a Pineapple pie in thread(4)

I get a Apple in thread(1)

I get a Banana in thread(0)

I get a Pear in thread(2)

I get a Plum in thread(3)

I get a Pineapple in thread(4)
BlockQueueSuite: 2/2, 100% correct in 0.021808 seconds
Total: 2 tests, 100% correct in 0.021808 seconds
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

说明:

  • 如上所述只运行测试用例BlockQueueSuite
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/775188
推荐阅读
相关标签
  

闽ICP备14008679号