目录
- 1 背景
- 2 设计
- 3 实现
- 4 使用
- 4.1 主函数
- 4.2 测试用例
- 4.2.1 定义
- 4.2.2 实现
- 4.3 运行
1 背景
前面文章CppTest实战演示中讲述如何使用CppTest库。其主函数如下:
int main(int argc, char *argv[])
{Test::Suite mainSuite;Test::TextOutput output(Test::TextOutput::Verbose);mainSuite.add(std::unique_ptr<Test::Suite>(new SeesionSuite));mainSuite.run(output, true);return 0;
}
以上代码有一点不好,就是每增加一个测试Suite就需要在main函数中调用mainSuite.add增加用例。有没有办法测试Suite自动添加,不需要修改main函数。下面讲述的测试框架可以解决这个问题。
2 设计
首先设计类TestApp,该类是单例模式,可以添加测试Suite,其次AutoAddSuite是一模板类在其构造函数中自动添加测试Suite.
其类图如下:
类定义如下:
class TestApp
{Test::Suite mainSuite_;TestApp();
public:static TestApp& Instance();void addSuite(Test::Suite * suite);int run(int argc, char *argv[]);
};
#define theTestApp TestApp::Instance()template<typename Suite>
class AutoAddSuite
{Suite* suite;
public:AutoAddSuite(): suite(new Suite()){ theTestApp.addSuite(suite);}
};
#define ADD_SUITE(Type) AutoAddSuite<Type> add##Type
说明:
- TestApp类型是单例类,提高增加Suite接口和run接口
- AutoAddSuite是一个自动添加Suite的模板类型
- 宏ADD_SUITE定义了AutoAddSuite对象,用于自动添加。
3 实现
#include "testapp.h"#include <iostream>
#include <cstring>
#include <cstdio>namespace
{
void usage()
{std::cout << "usage: test [MODE]\n"<< "where MODE may be one of:\n"<< " --compiler\n"<< " --html\n"<< " --text-terse (default)\n"<< " --text-verbose\n";exit(0);
}std::unique_ptr<Test::Output> cmdline(int argc, char* argv[])
{if (argc > 2)usage(); // will not returnTest::Output* output = 0;if (argc == 1)output = new Test::TextOutput(Test::TextOutput::Verbose);else{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{std::cout << "invalid commandline argument: " << arg << std::endl;usage(); // will not return}}return std::unique_ptr<Test::Output>(output);
}
}TestApp & TestApp::Instance()
{static TestApp theApp;return theApp;
}TestApp::TestApp()
{}void TestApp::addSuite(Test::Suite * suite)
{mainSuite_.add(std::unique_ptr<Test::Suite>(suite));
}int TestApp::run(int argc, char *argv[])
{try{std::unique_ptr<Test::Output> output(cmdline(argc, argv));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到mainSuite_
- run
- 首先根据命令行返回Test::Output
- 然后调用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 测试用例
这里使用C++标准库中std::mutex作为测试示例.
4.2.1 定义
#ifndef MUTEX_TEST_H
#define MUTEX_TEST_H
#include <cpptest/cpptest.h>class MutexSuite : public Test::Suite
{
public:MutexSuite(){TEST_ADD(MutexSuite::construct)TEST_ADD(MutexSuite::lock)TEST_ADD(MutexSuite::try_lock)TEST_ADD(MutexSuite::unlock)}void construct();void lock();void try_lock();void unlock();
};
#endif
说明:
- cpptest库标准使用,不再详述。
4.2.2 实现
#include "mutex_test.h"
#include "testapp.h"#include <thread>
#include <mutex>ADD_SUITE(MutexSuite);void addCount(std::mutex & mutex, int & count)
{mutex.lock();count++;mutex.unlock();
}void MutexSuite::construct()
{std::mutex muxtex;int count = 0;std::thread threads[10];for(int i = 0; i < 10; i++)threads[i] = std::thread(addCount, std::ref(muxtex), std::ref(count));for(auto &thread : threads)thread.join();TEST_ASSERT_EQUALS(10, count)
}void MutexSuite::lock()
{std::mutex muxtex;int count = 10;std::thread threads[10];for(int i = 0; i < 10; i++)threads[i] = std::thread(addCount, std::ref(muxtex), std::ref(count));for(auto &thread : threads)thread.join();TEST_ASSERT_EQUALS(20, count)
}struct Function
{volatile int counter = 0;void add_10k_count(std::mutex & muxtex){for(int i = 0; i < 10000; i++){if(muxtex.try_lock()){++counter;muxtex.unlock();}}}
};void MutexSuite::try_lock()
{std::mutex muxtex;Function function;std::thread threads[10];for(int i = 0; i < 10; i++)threads[i] = std::thread(&Function::add_10k_count, std::ref(function), std::ref(muxtex));for(auto &thread : threads)thread.join();TEST_ASSERT_EQUALS(true, function.counter < (10 * 10000))std::cerr << "function.counter: " << function.counter << std::endl;
}void MutexSuite::unlock()
{std::mutex muxtex;int count = 20;std::thread threads[10];for(int i = 0; i < 10; i++)threads[i] = std::thread(addCount, std::ref(muxtex), std::ref(count));for(auto &thread : threads)thread.join();TEST_ASSERT_EQUALS(30, count)
}
说明:
- 重点说明下文件头部宏ADD_SUITE的使用,如下所示传递给ADD_SUITE的参数正是MutexSuite,通过该定义,将MutexSuite自动添加到TestApp实例中,不需要修改main函数.
ADD_SUITE(MutexSuite);
4.3 运行
$ testapp --html
说明:
- testapp是编译出的可执行文件
- 参数–html 说明测试报告按网页格式输出.