我正在重构一些实现公式的代码,我想先测试,以提高我的测试技能,并保留代码。
这段特殊代码是一个公式,它接受3个参数并返回一个值。我甚至有一些数据表,其中包含不同输入的预期结果,因此从理论上讲,我可以进行多种类型的测试,只需更改输入参数并根据相应的预期值检查结果。
但我认为应该有更好的方法,并查看我发现的值参数化测试文档。
因此,我现在知道如何自动为不同的输入创建测试 但是,如何将相应的预期结果与我计算出的结果进行比较?
我唯一能想到的是静态查找表和文本夹具中的静态成员,它是查找表的索引,并在每次运行时递增。像这样:
#include "gtest/gtest.h"
double MyFormula(double A, double B, double C)
{
return A*B - C*C; // Example. The real one is much more complex
}
class MyTest:public ::testing::TestWithParam<std::tr1::tuple<double, double, double>>
{
protected:
MyTest(){ Index++; }
virtual void SetUp()
{
m_C = std::tr1::get<0>(GetParam());
m_A = std::tr1::get<1>(GetParam());
m_B = std::tr1::get<2>(GetParam());
}
double m_A;
double m_B;
double m_C;
static double ExpectedRes[];
static int Index;
};
int MyTest::Index = -1;
double MyTest::ExpectedRes[] =
{
// C = 1
// B: 1 2 3 4 5 6 7 8 9 10
/*A = 1*/ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
/*A = 2*/ 1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0,
/*A = 3*/ 2.0, 5.0, 8.0, 11.0, 14.0, 17.0, 20.0, 23.0, 26.0, 29.0,
// C = 2
// B: 1 2 3 4 5 6 7 8 9 10
/*A = 1*/ -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0,
/*A = 2*/ -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0,
/*A = 3*/ -1.0, 2.0, 5.0, 8.0, 11.0, 14.0, 17.0, 20.0, 23.0, 26.0,
};
TEST_P(MyTest, TestFormula)
{
double res = MyFormula(m_A, m_B, m_C);
ASSERT_EQ(ExpectedRes[Index], res);
}
INSTANTIATE_TEST_CASE_P(TestWithParameters,
MyTest,
testing::Combine( testing::Range(1.0, 3.0), // C
testing::Range(1.0, 4.0), // A
testing::Range(1.0, 11.0) // B
));
这是一个好方法还是有更好的方法来为每次运行获得正确的预期结果?
答案 0 :(得分:10)
包括预期结果和输入。而不是三倍的输入值,使您的测试参数为4元组。
class MyTest: public ::testing::TestWithParam<
std::tr1::tuple<double, double, double, double>>
{ };
TEST_P(MyTest, TestFormula)
{
double const C = std::tr1::get<0>(GetParam());
double const A = std::tr1::get<1>(GetParam());
double const B = std::tr1::get<2>(GetParam());
double const result = std::tr1::get<3>(GetParam());
ASSERT_EQ(result, MyFormula(A, B, C));
}
缺点是您无法使用testing::Combine
简化测试参数。相反,您可以使用testing::Values
来定义要测试的每个不同的4元组。您可能会达到Values
的参数计数限制,因此您可以拆分实例化,例如将所有 C = 1 个案放在一个并且所有 C = 2 在另一个案件中。
INSTANTIATE_TEST_CASE_P(
TestWithParametersC1, MyTest, testing::Values(
// C A B
make_tuple( 1.0, 1.0, 1.0, 0.0),
make_tuple( 1.0, 1.0, 2.0, 1.0),
make_tuple( 1.0, 1.0, 3.0, 2.0),
// ...
));
INSTANTIATE_TEST_CASE_P(
TestWithParametersC2, MyTest, testing::Values(
// C A B
make_tuple( 2.0, 1.0, 1.0, -3.0),
make_tuple( 2.0, 1.0, 2.0, -2.0),
make_tuple( 2.0, 1.0, 3.0, -1.0),
// ...
));
或者您可以将所有值放在与实例化分开的数组中,然后使用testing::ValuesIn
:
std::tr1::tuple<double, double, double, double> const FormulaTable[] = {
// C A B
make_tuple( 1.0, 1.0, 1.0, 0.0),
make_tuple( 1.0, 1.0, 2.0, 1.0),
make_tuple( 1.0, 1.0, 3.0, 2.0),
// ...
make_tuple( 2.0, 1.0, 1.0, -3.0),
make_tuple( 2.0, 1.0, 2.0, -2.0),
make_tuple( 2.0, 1.0, 3.0, -1.0),
// ...
};
INSTANTIATE_TEST_CASE_P(
TestWithParameters, MyTest, ::testing::ValuesIn(FormulaTable));
答案 1 :(得分:1)
请参阅硬编码预期结果就像您再次限制测试用例一样。如果你想获得一个完整的数据驱动模型,我宁愿建议你从平面文件/ xml / xls文件中读取输入,预期结果。
答案 2 :(得分:0)
我对单元测试没有太多经验,但作为一名数学家,我认为你可以做的更多。
如果你知道你的公式的一些不变量,你可以测试它们,但我认为这只在很少的情况下才有意义。
例如,如果你想测试,如果你已经正确地实现了自然指数函数,你可以利用这些知识,它的导数应该与函数本身具有相同的值。然后,您可以计算一百万个点的导数的数值近似值,看它们是否接近实际函数值。