可以用Googletest值参数化多个,不同类型的参数匹配mbUnit的灵活性

Can Googletest value-parameterized with multiple, different types of parameters match mbUnit flexibility?

本文关键字:参数 同类型 灵活性 mbUnit Googletest 值参      更新时间:2023-10-16

我想编写C++Google测试,它可以使用具有不同数据类型的多个参数的值参数化测试,与以下用C++/CLI编写的mbUnit测试的复杂性完美匹配。

有关mbUnit的解释,请参阅Hanselman 2006年的文章。截至2019年的编辑,他包含的其他链接都已失效。

注意这是多么紧凑,[Test]属性表示这是一个测试方法,[Row(...)]属性定义实例化的值。

[Test]
[Row("Empty.mdb", "select count(*) from collar", 0)]
[Row("SomeCollars.mdb", "select count(*) from collar", 17)]
[Row("SomeCollars.mdb", "select count(*) from collar where max_depth=100", 4)]
void CountViaDirectSQLCommand(String^ dbname, String^ command, int numRecs)
{
   String^ dbFilePath = testDBFullPath(dbname);
   {
       StAnsi fpath(dbFilePath);
       StGdbConnection db( fpath );
       db->Connect(fpath);
       int result = db->ExecuteSQLReturningScalar(StAnsi(command));
       Assert::AreEqual(numRecs, result);
   }
}

或者更好的是,C#的这种更奇特的测试(将.Net属性中可以定义的内容的边界推到C++/CLI中可能定义的范围之外):

[Test]
[Row("SomeCollars.mdb", "update collar set x=0.003 where hole_id='WD004'", "WD004",
    new string[] { "x", "y" },
    new double[] { 0.003, 7362.082 })]  // y value unchanged 
[Row("SomeCollars.mdb", "update collar set x=1724.8, y=6000 where hole_id='WD004'", "WD004",
    new string[] { "x", "y" },
    new double[] { 1724.8, 6000.0 })]
public void UpdateSingleRowByKey(string dbname, string command, string idValue, string[] fields, double[] values)
{
...
}

帮助中说,值参数化测试只允许您编写一次测试,然后使用任意数量的参数值轻松地实例化和运行它但我相当确定这是指测试用例的数量。

即使不改变数据类型,在我看来,参数化测试也只能接受一个参数?

2019更新

添加是因为我收到了关于这个问题的ping。显示的Row属性是mbUnit的一部分。

有关mbUnit的解释,请参阅Hanselman 2006年的文章。截至2019年的编辑,他包含的其他链接都已失效。

在C#世界中,NUnit以一种更强大、更灵活的方式添加了参数化测试,包括将泛型处理为参数化固定装置。

以下测试将执行十五次,每个x值执行三次,每次结合从-1.0到+1.0的5次随机加倍。

[Test]
public void MyTest(
    [Values(1, 2, 3)] int x,
    [Random(-1.0, 1.0, 5)] double d)
{
    ...
}

下面的测试夹具将由NUnit实例化三次,将每组参数传递给相应的构造函数。请注意,有三种不同的构造函数,它们与作为参数提供的数据类型相匹配。

[TestFixture("hello", "hello", "goodbye")]
[TestFixture("zip", "zip")]
[TestFixture(42, 42, 99)]
public class ParameterizedTestFixture
{
    private string eq1;
    private string eq2;
    private string neq;
    
    public ParameterizedTestFixture(string eq1, string eq2, string neq)
    {
        this.eq1 = eq1;
        this.eq2 = eq2;
        this.neq = neq;
    }
    public ParameterizedTestFixture(string eq1, string eq2)
        : this(eq1, eq2, null) { }
    public ParameterizedTestFixture(int eq1, int eq2, int neq)
    {
        this.eq1 = eq1.ToString();
        this.eq2 = eq2.ToString();
        this.neq = neq.ToString();
    }
    [Test]
    public void TestEquality()
    {
        Assert.AreEqual(eq1, eq2);
        if (eq1 != null && eq2 != null)
            Assert.AreEqual(eq1.GetHashCode(), eq2.GetHashCode());
    }
    [Test]
    public void TestInequality()
    {
        Assert.AreNotEqual(eq1, neq);
        if (eq1 != null && neq != null)
            Assert.AreNotEqual(eq1.GetHashCode(), neq.GetHashCode());
    }
}

是的,只有一个参数。不过,您可以使该参数任意复杂。您可以调整文档中的代码以使用Row类型,例如:

class AndyTest : public ::testing::TestWithParam<Row> {
  // You can implement all the usual fixture class members here.
  // To access the test parameter, call GetParam() from class
  // TestWithParam<T>.
};

然后定义您的参数化测试:

TEST_P(AndyTest, CountViaDirectSQLCommand)
{
  // Call GetParam() here to get the Row values
  Row const& p = GetParam();
  std::string dbFilePath = testDBFullPath(p.dbname);
  {
    StAnsi fpath(dbFilePath);
    StGdbConnection db(p.fpath);
    db.Connect(p.fpath);
    int result = db.ExecuteSQLReturningScalar(StAnsi(p.command));
    EXPECT_EQ(p.numRecs, result);
  }
}

最后,实例化它:

INSTANTIATE_TEST_CASE_P(InstantiationName, AndyTest, ::testing::Values(
  Row("Empty.mdb", "select count(*) from collar", 0),
  Row("SomeCollars.mdb", "select count(*) from collar", 17),
  Row("SomeCollars.mdb", "select count(*) from collar where max_depth=100", 4)
));

使用自定义结构作为参数的另一种选择是使用参数生成器::testing::Combine(g1, g2, ..., gn)。此生成器允许您将其他参数生成器组合为一组类型为std::tuple的参数,该参数的模板类型与提供的值的类型相匹配。

请注意,此生成器生成所提供值的笛卡尔乘积。这意味着将创建所有可能的有序元组。我相信最初的问题是要求使用所提供的值来创建一个严格的参数数组,这是不支持的。如果需要一个严格参数数组,可以使用参数生成器::testing::Values(v1, v2, ..., vN)的元组,其中每个值都是一个单独的元组。

示例:

#include <string>
#include <tuple>
class MyTestSuite : 
  public testing::TestWithParam<std::tuple<std::string, std::string, int>>
{
};
TEST_P(MyTestSuite, TestThatThing)
{
  functionUnderTest(std::get<0>(GetParam()), 
                    std::get<1>(GetParam()), 
                    std::get<2>(GetParam()));
  . . .
}
INSTANTIATE_TEST_SUITE_P(
  MyTestGroup,
  MyTestSuite,
  ::testing::Combine(
    ::testing::Values("FirstString1", "FirstString2"),
    ::testing::Values("SecondString1", "SecondString2"),
    ::testing::Range(10, 13)));
INSTANTIATE_TEST_SUITE_P(
  MyOtherTestGroupThatUsesStrictParameters,
  MyTestSuite,
  ::testing::Values(
    {"FirstString1", "SecondString1", 10},
    {"FirstString2", "SecondString2", 32},
    {"FirstString3", "SecondString3", 75}));

在上面的示例中,为MyTestGroup创建的参数如下所示:

[
  {"FirstString1", "SecondString1", 10},
  {"FirstString1", "SecondString1", 11},
  {"FirstString1", "SecondString1", 12},
  {"FirstString1", "SecondString2", 10},
  {"FirstString1", "SecondString2", 11},
  {"FirstString1", "SecondString2", 12},
  {"FirstString2", "SecondString1", 10},
  {"FirstString2", "SecondString1", 11},
  {"FirstString2", "SecondString1", 12},
  {"FirstString2", "SecondString2", 10},
  {"FirstString2", "SecondString2", 11},
  {"FirstString2", "SecondString2", 12}
]

有关更多详细信息,请参阅GoogleTest文档。(2019年12月17日访问)