目錄
- 1 背景
- 2 設計
- 3 實現
- 4 使用
- 4.1 主函數
- 4.2 使用方法
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);
說明:
- 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);elsestd::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;
}
說明:
- 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;
}
主函數很簡單,變化。
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):AtomicSuiteBlockQueueSuiteConditionVariableSuiteFutureSuiteLocksSuiteMutexSuiteRingQueueSuiteThreadSuiteTimedMutexSuite
運行測試用例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 pieI get a Apple pie
I get a Banana pie
I get a Pear pie
I get a Plum pie
I get a Pineapple pieI 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
說明:
- 如上所述只運行測試用例BlockQueueSuite