我的组织正在使用CppUnit,我正在尝试使用不同的参数运行相同的测试。在测试中运行循环不是一个好选择,因为任何失败都会中止测试。我看过TestDecorator
和TestCaller
,但似乎都不合适。代码示例会有所帮助。
答案 0 :(得分:8)
在CppUnit中似乎不可能直接参数化测试用例(参见here和here)。但是,您有几个选择:
RepeatedTest
您可以巧妙地使用内置的RepeatedTest
装饰器。这允许测试用例多次运行(尽管没有参数化)。
我承认自己从来没有使用过这个,但也许你可以让RepeatedTest
驱动一些看门人功能,这会(使用类静态变量,也许?)每次运行都会选择不同的输入。它会反过来调用你想用该值作为输入进行测试的真实函数。
TestCase
子类 One person声称编写了TestCase
的子类,它将以任意次数运行特定测试,但方式与RepeatedTest
类略有不同提供。可悲的是,海报简单地描述了创建类的动机,但没有提供源代码。但是,有人要求与个人联系以获取更多详细信息。
最直接(但最不自动化)的方法是创建一个辅助函数,将您想要传递的参数传递给您的“真实”函数,然后有很多单独的测试用例。每个测试用例都会使用不同的值调用辅助函数。
如果您选择上面列出的前两个选项中的任何一个,我会有兴趣了解您的体验。
答案 1 :(得分: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的“首选做事方式”,我不知道这是否被视为犹太人,但这就是我现在采取的方法。
答案 2 :(得分: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);
详细说明可在此处找到:http://brain-child.de/engineering/parameterizing-cppunit-tests
德语版可以在这里找到:http://brain-child.de/engineering/parametrierbare-tests-cppunit
答案 3 :(得分:0)
我不是C ++程序员,但我可以帮助解决单元测试问题:
测试用例意味着隔离运行,不依赖于外部参数。此外,您应该将测试用例的数量保持在最低限度,以涵盖大部分代码。然而,有些情况(我已经处理了一些),其中一些测试看起来相同,只是一些小参数不同。最好的办法是编写一个 fixture ,它接受你正在讨论的参数,然后为每个参数设置一个测试用例,用它调用fixture。一个通用的例子如下:
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
否则你不会编写真正的测试用例,因为它们应该是可重现的,而不需要执行它们的人那么多的知识。我想有一些参数作为测试的输入都很重要。那为什么不在自己的测试用例中明确每一个呢?这也是记录文档的最佳方式,而不是编写单独的文档来指导程序员,该程序员将在几年后读取代码。
答案 4 :(得分:0)
这是一个非常古老的问题,但我只需要做类似的事情并提出以下解决方案。我对此并不是百分之百满意,但它似乎很好地完成了这项工作
为测试方法定义一组输入参数。例如,假设这些是字符串,那么让我们这样做:
std::vector<std::string> testParameters = { "string1", "string2" };
size_t testCounter = 0;
实现通用测试器函数,每次调用时都会从测试数组中获取下一个参数,例如:
void Test::genericTester()
{
const std::string ¶m = testParameters[testCounter++];
// do something with param
}
在测试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(),每个参数一个,指定名称。这对我来说似乎很有用。
希望这有助于某人。
答案 5 :(得分:0)
根据消费者的回答,我最终得到了一个非常好的方法,我可以使用一行注册宏创建多个测试,其中包含我想要的任意数量的参数。
只需定义一个参数类:
class Param
{
public:
Param( int param1, std::string param2 ) :
m_param1( param1 ),
m_param2( param2 )
{
}
int m_param1;
std::string m_param2;
};
让您的测试夹具将其用作&#34;非类型模板参数&#34; (我认为这是如何被称为):
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
答案 6 :(得分: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 ®istry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest( registry.makeTest() );
bool wasSuccessful = runner.run( "", false );
return wasSuccessful;
}