[英]How to write a script to perform automatic test with boost::unit_test?
I am a newbie of automatic unit test in C++. 我是C ++中自动单元测试的新手。 I have followed the instruction of boost::unit_test and finished a test scheme by calling function
unit_test_main
in boost::unit_test. 我遵循了boost :: unit_test的指令,并通过在boost :: unit_test中调用function
unit_test_main
完成了一个测试方案。 It is no problem for me to run the test program. 对我来说,运行测试程序没有问题。 However, I have problems with pass the arguments to the test function.
但是,我在将参数传递给测试函数时遇到问题。 Maybe, the following codes can illustrate my problem much better:
也许,以下代码可以更好地说明我的问题:
#ifndef MAIN_CPP_
#define MAIN_CPP_
#include <string>
#include <vector>
#include <iostream>
#include <assert.h>
#include <boost/program_options.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/test/execution_monitor.hpp>
#include <boost/test/unit_test.hpp>
using namespace boost::program_options;
using namespace std;
using namespace boost;
using namespace boost::unit_test;
/**
* the global test suite
*/
boost::unit_test::test_suite* get_feelfree_test_suite();
boost::unit_test::test_suite* main_global_test_suite;
/**
* name of the test suite
*/
std::string current_global_test_suite_name;
#ifdef BOOST_TEST_ALTERNATIVE_INIT_API
bool run_global_test_suite () {
boost::unit_test::test_suite& masterTestSuite = framework::master_test_suite();
if(masterTestSuite.size() != 0) {
test_unit_id formerTestSuite = masterTestSuite.get(current_global_test_suite_name);
masterTestSuite.remove(formerTestSuite);
}
masterTestSuite.add(main_global_test_suite);
current_global_test_suite_name = main_global_test_suite->p_name.get();
return true;
}
#else
test_suite* run_global_test_suite(int, char* []) {
return main_global_test_suite;
}
#endif
/**
* Obtain test program options
*/
int obtain_options(char **optionLine, int argc, char** argv);
/**
* This function is used to run the test program, and the procedure is really standard.
*/
int main( int argc, char* argv[] )
{
try
{
/**
* Step 1. obtain options
*/
char* optionLine[1024];
int len ;
len = obtain_options(optionLine, argc, argv);
/**
* Step 2. perform unit test based on user's options
*/
int test_status=0;
main_global_test_suite = get_feelfree_test_suite();
test_status = unit_test_main(run_global_test_suite, len, optionLine);
return test_status;
}
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
return 1;
}
catch (const std::string& s)
{
std::cout << s << std::endl;
return 1;
}
catch (...)
{
return 1;
}
}
/** @} */
int obtain_options(char **optionLine, int argc, char* argv[])
{
// 1. All the options for testing the program
options_description desc("Allowed options");
desc.add_options()("help", "produce help message")
("detect_memory_leaks", value<bool>()->default_value(false), "test configuration option (option of boost framework)");
// 2. Perform parsing
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
notify(vm);
// 3. Illustrate the input
std::vector<const char*> options;
std::string testSuiteToRun;
if(vm.count("test_suite")){
testSuiteToRun = vm["test_suite"].as<string>();
}
else {
testSuiteToRun = "main";
}
options.push_back(argv[0]);
if(vm.count("detect_memory_leaks")) {
bool detect = vm["detect_memory_leaks"].as<bool>();
if(detect) {
options.push_back("--detect_memory_leaks=1");
}
else {
options.push_back("--detect_memory_leaks=0");
}
}
else {
options.push_back("--detect_memory_leaks=0");
}
// 4. Obtain all the parameters in the format of char**
assert(options.size() < 1024);
std::copy(options.begin(), options.end(), const_cast<const char**>(optionLine));
return options.size();
}
void Testsub(const std::string &name)
{
cout<<"File_name: "<<name<<endl;
}
void Testabc( )
{
std::vector<std::string > name_array;
name_array.push_back("name 1");
name_array.push_back("name 2");
for(int i=0; i<name_array.size(); i++)
Testsub(name_array[i]);
}
boost::unit_test::test_suite* get_feelfree_test_suite()
{
test_suite* ts = BOOST_TEST_SUITE( "unit_geometric" );
ts->add( BOOST_TEST_CASE(&Testabc) );
return ts;
}
#endif
As you can see, in this test framework, the main function I want to test is Testsub , which relies on the input argument const std::string &name . 如您所见,在此测试框架中,我要测试的主要功能是Testsub ,它依赖于输入参数const std :: string&name 。 However, I can not pass any arguments via the test suite function get_feelfree_test_suite .
但是,我无法通过测试套件函数get_feelfree_test_suite传递任何参数。 Therefore, in this test program, I wrote another test function Testabc , where all the possible file test lists are given and passed to Testsub .
因此,在此测试程序中,我编写了另一个测试函数Testabc ,其中给出了所有可能的文件测试列表,并将其传递给Testsub 。 This is definitely not the best solution.
这绝对不是最佳解决方案。 I am wondering whether there are other solutions.
我想知道是否还有其他解决方案。 Several solutions are in my mind, but I do not know whether they are good solutions:
我想到了几种解决方案,但我不知道它们是否是好的解决方案:
int main( int argc, char* argv[]
). After that, write a script to run the program several times. In windows, one possible script is .bat script. For this solution, I do not know how to implement it. int main( int argc, char* argv[]
)将参数传递给get_feelfree_test_suite的方法。此后,编写脚本多次运行该程序。在Windows中,一种可能脚本是.bat脚本,对于这种解决方案,我不知道如何实现。 I also hear that Python can be very easily incorporated in the test framework, but I have no idea about that. 我还听说Python可以很容易地集成到测试框架中,但是我对此一无所知。 Anyway, I am open to all the possible solutions, and thanks!
无论如何,我愿意接受所有可能的解决方案,谢谢!
Do you really need to have the different "names" in a separate file? 您是否真的需要在单独的文件中使用不同的“名称”? It would probably simpler to put them into your test suite.
将它们放入测试套件可能会更简单。 One BOOST_AUTO_TEST_CASE for every name.
每个名称一个BOOST_AUTO_TEST_CASE。 Or an array of names, which you can iterate over in the test case.
或一个名称数组,您可以在测试用例中对其进行迭代。
Since I do not really understand the purpose of all the code, I post a minimal sample that does the work (calling Testsub
several times) by parsing elements like -F file1 -F file2
from the command line. 由于我并不是很了解所有代码的用途,因此我发布了一个最小的示例,该示例通过
Testsub
解析-F file1 -F file2
类的元素来完成工作(多次调用Testsub
)。 It does use the BOOST_PARAM_TEST_CASE
macro of boost::unittest
: 它确实使用
boost::unittest
的BOOST_PARAM_TEST_CASE
宏:
#include <boost/test/parameterized_test.hpp>
//...
void Testsub(const std::string &name)
{
cout<<"File_name: "<<name<<endl;
}
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
std::vector<std::string> files_to_run_local;
for(int i = 0; i < framework::master_test_suite().argc; i++)
{
if(std::string(framework::master_test_suite().argv[i]) == "-F")
{
if(i == framework::master_test_suite().argc - 1)
{
std::cerr << "Error in the command line" << std::endl;
throw boost::unit_test::framework::setup_error("Error in the command line");
}
files_to_run_local.push_back(framework::master_test_suite().argv[++i]);
}
}
test_suite* ts = BOOST_TEST_SUITE( "unit_geometric" );
ts->add( BOOST_PARAM_TEST_CASE( &Testsub,
files_to_run_local.begin(),
files_to_run_local.end() ) );
framework::master_test_suite().add(ts);
return 0;
}
Now, I think you are the only one to decide over the method used to pass the list of files to the unit test framework. 现在,我认为您是唯一决定将文件列表传递给单元测试框架的方法的人。 A file containing all files would be also a good solution, and might be appropriate if the list to pass is big, but has the disadvantage of using an intermediate file to do that.
包含所有文件的文件也是一个很好的解决方案,如果要传递的列表很大,则可能是适当的选择,但缺点是使用中间文件来执行此操作。
But answering that question really depends on what framework is driving your tests (cmake, shell, etc.) Python/cmake can generate either the command line or the intermediate file very easily for you. 但是,回答这个问题实际上取决于驱动测试的框架(cmake,shell等)。Python/ cmake可以非常轻松地生成命令行或中间文件。
In all case, the clean method is to call the BOOST_PARAM_TEST_CASE
macro. 在所有情况下,clean方法都是调用
BOOST_PARAM_TEST_CASE
宏。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.