1

我是 C++ 中自动单元测试的新手。我按照 boost::unit_test 的指令,通过调用unit_test_mainboost::unit_test 中的函数完成了一个测试方案。运行测试程序对我来说没有问题。但是,我在将参数传递给测试函数时遇到了问题。也许,以下代码可以更好地说明我的问题:

#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

如您所见,在这个测试框架中,我要测试的主要函数是Testsub,它依赖于输入参数const std::string &name。但是,我不能通过测试套件函数get_feelfree_test_suite传递任何参数。因此,在这个测试程序中,我编写了另一个测试函数Testabc,其中给出了所有可能的文件测试列表并传递给Testsub。这绝对不是最好的解决方案。我想知道是否还有其他解决方案。我想到了几个解决方案,但我不知道它们是否是好的解决方案:

  • 解决方案 1:尝试找出一种方法将参数 从主函数 ( )传递给get_feelfree_test_suite 。int main( int argc, char* argv[]之后,编写一个脚本来多次运行该程序。在 Windows 中,一种可能的脚本是 .bat 脚本。对于这个解决方案,我不知道如何实现它。
  • 解决方案2:编写一个列表文件,其中给出所有可能的输入文件测试名称,然后在程序中读取该列表文件。这更容易实现。

我还听说 Python 可以很容易地集成到测试框架中,但我对此一无所知。无论如何,我对所有可能的解决方案持开放态度,谢谢!

4

2 回答 2

0

您真的需要在单独的文件中包含不同的“名称”吗?将它们放入您的测试套件可能会更简单。每个名称一个 BOOST_AUTO_TEST_CASE。或者一个名称数组,您可以在测试用例中对其进行迭代。

于 2012-10-05T00:30:28.393 回答
0

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. It does use the BOOST_PARAM_TEST_CASE macro of boost::unittest:

#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.

In all case, the clean method is to call the BOOST_PARAM_TEST_CASE macro.

于 2012-10-22T12:42:24.460 回答