7

我的组织正在使用 CppUnit,我正在尝试使用不同的参数运行相同的测试。在测试中运行循环不是一个好的选择,因为任何失败都会中止测试。我看过,TestDecoratorTestCaller似乎都不太合适。代码示例会很有帮助。

4

7 回答 7

8

It does not appear possible in CppUnit to parameterize a test case directly (see here and here). However, you do have a few options:

Use a RepeatedTest

You may be able to make some clever use of the built-in RepeatedTest decorator. This allows a test case to be run multiple times (though without parameterization).

I'll admit to never having used this myself, but perhaps you could have the RepeatedTest drive some gatekeeper function, which would (using a class static variable, perhaps?) pick a different input with every run. It would in turn call the true function you'd like to test with that value as input.

Use a TestCase subclass

One person on CppUnit's SourceForge page claims to have written a subclass of TestCase that will run a particular test an arbitrary number of times, although in a slightly different manner than the RepeatedTest class offers. Sadly, the poster simply described the motivation for creating the class, but did not provide the source code. There was, however, an offer to contact the individual for more details.

Use a simple helper function

The most straight-forward (but least automated) way to do this is to create a helper function that takes the parameter you'd like to pass on to your "real" function, and then have lots of individual test cases. Each test case would call your helper function with a different value.


If you choose either of the first two options listed above, I'd be interested in hearing about your experience.

于 2008-11-14T21:13:56.007 回答
3
class members : public CppUnit::TestFixture
{
    int i;
    float f;
};

class some_values : public members
{
    void setUp()
    {
        // initialization here
    }
};

class different_values : public members
{
    void setUp()
    {
        // different initialization here
    }
};

tempalte<class F>
class my_test : public F
{
    CPPUNIT_TEST_SUITE(my_test<F>);
    CPPUNIT_TEST(foo);
    CPPUNIT_TEST_SUITE_END();

    foo() {}
};

CPPUNIT_TEST_SUITE_REGISTRATION(my_test<some_values>);
CPPUNIT_TEST_SUITE_REGISTRATION(my_test<different_values>);

我不知道根据 CppUnit 的“首选做事方式”,这是否被认为是犹太洁食,但这就是我现在采用的方法。

于 2010-05-17T22:43:34.110 回答
1

根据 Marcin 的建议,我实现了一些宏来帮助定义参数化 CppUnit 测试。

使用此解决方案,您只需替换类头文件中的旧宏 CPPUNIT_TEST_SUITE 和 CPPUNIT_TEST_SUITE_END:

CPPUNIT_PARAMETERIZED_TEST_SUITE(<TestSuiteClass>, <ParameterType>);

/*
 * put plain old tests here.
 */

CPPUNIT_PARAMETERIZED_TEST_SUITE_END();

在实现文件中,您需要将旧的 CPPUNIT_TEST_SUITE_REGISTRATION 宏替换为:

CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION ( <TestSuiteClass>, <ParameterType> )

这些宏要求您实现以下方法:

static std::vector parameters();
void testWithParameter(ParameterType& parameter);
  • parameters():提供带有参数的向量。
  • testWithParameter(...):为每个参数调用。这是您实施参数化测试的地方。

可以在这里找到详细的解释:http: //brain-child.de/engineering/parameterizing-cppunit-tests

德语版本可以在这里找到:http: //brain-child.de/engineering/parametrierbare-tests-cppunit

于 2014-02-26T07:32:27.410 回答
0

这是一个非常古老的问题,但我只需要做类似的事情并提出以下解决方案。我对它不是 100% 满意,但它似乎做得很好

  1. 为测试方法定义一组输入参数。例如,假设这些是字符串,那么让我们这样做:

    std::vector<std::string> testParameters = { "string1", "string2" };
    size_t testCounter = 0;
    
  2. 实现一个通用测试器函数,每次调用都会从测试数组中获取下一个参数,例如:

    void Test::genericTester()
    {
      const std::string &param = testParameters[testCounter++];
    
      // do something with param
    } 
    
  3. 在测试 addTestToSuite() 方法声明(由 CPPUNIT 宏隐藏)而不是(或旁边)使用 CPPUNIT_TEST 宏定义方法时,添加类似于以下的代码:

    CPPUNIT_TEST_SUITE(StatementTest);
    
    testCounter = 0;
    for (size_t i = 0; i < testParameters.size(); i++) {
      CPPUNIT_TEST_SUITE_ADD_TEST(
        ( new CPPUNIT_NS::TestCaller<TestFixtureType>(
                  // Here we use the parameter name as the unit test name.
                  // Of course, you can make test parameters more complex, 
                  // with test names as explicit fields for example.
                  context.getTestNameFor( testParamaters[i] ),
                  // Here we point to the generic tester function.
                  &TestFixtureType::genericTester,
                  context.makeFixture() ) ) );
    }
    
    CPPUNIT_TEST_SUITE_END();
    

这样,我们多次注册 genericTester(),每个参数一个,并指定一个名称。这似乎对我很有效。

希望这可以帮助某人。

于 2013-04-17T23:57:15.803 回答
0

我不是 C++ 程序员,但我可以在单元测试概念方面提供帮助:

测试用例旨在独立运行,不依赖于外部参数。此外,您应该将测试用例的数量保持在涵盖大部分代码的最低限度。然而,在某些情况下(我已经处理过一些),一些测试看起来相同,只是一些次要参数不同。最好的办法是编写一个带有您正在谈论的参数的夹具,然后为每个参数设置一个测试用例,并用它调用夹具。一个通用的例子如下:

class MyTestCase

  # this is your fixture
  def check_special_condition(param)
    some
    complex
    tests
  end

  # these are your test-cases
  def test_1
    check_special_condition("value_1")
  end

  def test_2
    check_special_condition("value_2")
  end

end

否则,您就不会编写真正的测试用例,因为它们应该是可重现的,而执行它们的人没有太多知识。我想有一些参数作为测试的输入都很重要。那么为什么不在自己的测试用例中明确说明每一个呢?这也是最好的记录方式,而不是编写单独的文档来指导将在几年后阅读代码的程序员。

于 2008-11-14T14:49:14.693 回答
0

以下类/助手宏对适用于我当前的用例。在您的TestFixture子类中,只需定义一个接受一个参数的方法,然后使用PARAMETERISED_TEST(method_name, argument_type, argument_value).

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/ui/text/TestRunner.h>

template <class FixtureT, class ArgT>
class ParameterisedTest : public CppUnit::TestCase {
public:
  typedef void (FixtureT::*TestMethod)(ArgT);
  ParameterisedTest(std::string name, FixtureT* fix, TestMethod f, ArgT a) :
    CppUnit::TestCase(name), fixture(fix), func(f), arg(a) {
  }
  ParameterisedTest(const ParameterisedTest* other) = delete;
  ParameterisedTest& operator=(const ParameterisedTest& other) = delete;

  void runTest() {
    (fixture->*func)(arg);
  }
  void setUp() { 
    fixture->setUp(); 
  }
  void tearDown() { 
    fixture->tearDown(); 
  }
private:
  FixtureT* fixture;
  TestMethod func;
  ArgT arg;
};

#define PARAMETERISED_TEST(Method, ParamT, Param)           \
  CPPUNIT_TEST_SUITE_ADD_TEST((new ParameterisedTest<TestFixtureType, ParamT>(context.getTestNameFor(#Method #Param), \
                                          context.makeFixture(), \
                                          &TestFixtureType::Method, \
                                              Param)))

class FooTests : public CppUnit::TestFixture {
  CPPUNIT_TEST_SUITE(FooTests);
  PARAMETERISED_TEST(ParamTest, int, 0);
  PARAMETERISED_TEST(ParamTest, int, 1);
  PARAMETERISED_TEST(ParamTest, int, 2);
  CPPUNIT_TEST_SUITE_END();
public:
  void ParamTest(int i) {
    CPPUNIT_ASSERT(i > 0);
  }
};
CPPUNIT_TEST_SUITE_REGISTRATION(FooTests);

int main( int argc, char **argv)
{
  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest( registry.makeTest() );
  bool wasSuccessful = runner.run( "", false );
  return wasSuccessful;
}
于 2016-06-14T14:04:25.283 回答
0

根据 consumerwhore 的回答,我最终得到了一种非常好的方法,我可以使用带有尽可能多的参数的单行注册宏创建多个测试。

只需定义一个参数类:

class Param
{
public:
    Param( int param1, std::string param2 ) :
        m_param1( param1 ),
        m_param2( param2 )
    {
    }

    int m_param1;
    std::string m_param2;
};

让您的测试夹具将其用作“非类型模板参数”(我认为这就是它的名称):

template <Param& T>
class my_test : public CPPUNIT_NS::TestFixture
{
    CPPUNIT_TEST_SUITE(my_test<T>);
    CPPUNIT_TEST( doProcessingTest );
    CPPUNIT_TEST_SUITE_END();

    void doProcessingTest()
    {
        std::cout << "Testing with " << T.m_param1 << " and " << T.m_param2 << std::endl;
    };
};

有一个小宏创建一个参数并注册一个新的测试夹具:

#define REGISTER_TEST_WITH_PARAMS( name, param1, param2 ) \
    Param name( param1, param2 ); \
    CPPUNIT_TEST_SUITE_REGISTRATION(my_test<name>);

最后,像这样添加尽可能多的测试:

REGISTER_TEST_WITH_PARAMS( test1, 1, "foo" );
REGISTER_TEST_WITH_PARAMS( test2, 3, "bar" );

执行此测试将为您提供:

my_test<class Param test1>::doProcessingTestTesting with 1 and foo : OK
my_test<class Param test2>::doProcessingTestTesting with 3 and bar : OK
OK (2)
Test completed, after 0 second(s). Press enter to exit
于 2016-01-08T08:47:04.180 回答