我目前正在使用googles unit test framework为嵌入式应用程序编写单元测试。现在我的老板很不高兴我测试的数据(即我称之为测试类的方法的值)在测试中是硬接线的。他要求从文件中读取此数据。他的论点是,为一个以前被遗忘的角落案件添加另一个测试会更容易。我不是单位测试的经验,但到目前为止,我不是这样做的。所以我试着弄清楚最好的方法是什么 - 即使这样做是个好主意。我很快就遇到了DDT(数据驱动测试)方法。
谷歌单元测试框架有一个名为“Value-Parameterized Tests”的功能。有了它,我的测试夹具成为模板类,我可以传入参数。但是,我看到了一些问题:
我会想象像谷歌测试框架那样成熟的东西可以让它变得更容易。但是,他们写了
值参数化测试很方便[当]你想通过各种输入测试你的代码时(a.k.a.数据驱动的测试)。此功能很容易被滥用,所以请在练习时锻炼你的意识!
另外还有这篇博文TotT: Data Driven Traps,也警告我(滥用)数据驱动的单元测试。
所以我的问题归结为:
我并不一定非常喜欢googletest,但我基本上可以自由选择任何我喜欢的框架。
修改
我在googletest常见问题解答的常见问题解答条目中找到以下声明
一般来说,Google Test对数据驱动的测试还没有很好的支持。我们希望能尽快在这方面做出改进。
答案 0 :(得分:7)
GTest支持它 - 但也许他们不知道......
使用testing::ValuesIn
- 就像在这个简化示例中一样:
class SomeTests : public TestWithParam<int>
{
public:
};
TEST_P(SomeTests, shouldBePositive)
{
ASSERT_GT(GetParam(), 0);
}
并且 - 如何从输入流中获取值:
std::ifstream inputValuesFromFile("input.txt");
INSTANTIATE_TEST_CASE_P(FromFileStream,
SomeTests,
ValuesIn(std::istream_iterator<int>(inputValuesFromFile),
std::istream_iterator<int>()));
要使用比“int”更复杂的类型,您需要编写运算符&gt;&gt;为它 - 像:
struct A
{
int a;
std::vector<int> b;
}
std::istream& operator >> (std::istream& is, A& out)
{
std::size_t bSize;
if ((is >> A.a) && (is >> bSize))
{
out.b.reserve(bSize);
while (bSize-- > 0)
{
int b;
if (!(is >> b))
break;
out.b.push_back(b);
}
}
return is;
}
当然 - 在更复杂的情况下 - 考虑使用类似XMl(json?)格式和一些比std::istream_iterator<T>
更专业的迭代器。
对于类似XML的格式 - 你可能会考虑这样的方案(这是非常假设的代码 - 我脑子里没有任何这样的库):
SomeLib::File xmlData("input.xml");
class S1BasedTests : public TestWithParam<S1>
{};
TEST_P(S1BasedTests , shouldXxxx)
{
const S1& s1 = GetParam();
...
}
auto s1Entities = file.filterBy<S1>("S1");
INSTANTIATE_TEST_CASE_P(S1,
S1BasedTests,
ValuesIn(s1Entities.begin(), s1Entities .end()));
对于你想要的任何类型S1,// etc
如果市场上没有这样的C ++类型友好库(我搜索了2分钟但没找到) - 那么可能是这样的:
SomeLib::File xmlFile("input.xml");
struct S1BasedTests : public TestWithParam<SomeLib::Node*>
{
struct S1 // xml=<S1 a="1" b="2"/>
{
int a;
int b;
};
S1 readNode()
{
S1 s1{};
s1.a = GetParam()->getNode("a").getValue<int>();
s1.b = GetParam()->getNode("b").getValue<float>();
return s1;
}
};
TEST_P(S1BasedTests , shouldXxxx)
{
const S1& s1 = readNode();
...
}
INSTANTIATE_TEST_CASE_P(S1,
S1BasedTests ,
ValuesIn(xmlFile.getNode("S1").getChildren()));
// xml=<S1s> <S1.../> <S1.../> </S1>
对于任何节点类型(如S1 ),// etc