C++ 使用多个不同类型的参数进行 Googletest 值参数化能否与 mbUnit 的灵活性相匹配?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6256758/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:47:31  来源:igfitidea点击:

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

c++unit-testinggoogletest

提问by Andy Dent

I'd like to write C++ Google tests which can use value-parameterized testswith multiple parameters of different data types, ideally matching the complexity of the following mbUnit tests written in C++/CLI.

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

For an explanation of mbUnit, see the Hanselman 2006 article. As of this 2019 edit, the other links he includes are dead.

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

Note how compact this is, with the [Test]attribute indicating this is a test method and the [Row(...)]attributes defining the values for an instantiation.

请注意这是多么紧凑,[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);
   }
}

Or even better, this more exotic testing from C# (pushing the boundaries of what can be defined in .Net attributes beyond what's possible in C++/CLI):

或者甚至更好的是,这个来自 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)
{
...
}

The helpsays Value-parameterized tests will let you write your test only once and then easily instantiate and run it with an arbitrary number of parameter values.but I'm fairly certain that is referring to the number of test cases.

帮助下价值参数测试将让你写你的测试一次,然后轻松地实例化,并与参数值任意数量的运行。但我相当肯定这是指测试用例的数量。

Even without varying the data types, it seems to me that a parameterized test can only take oneparameter?

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

2019 update

2019年更新

Added because I got pinged about this question. The Rowattribute shown is part of mbUnit.

添加是因为我对这个问题很感兴趣。显示的Row属性是 mbUnit 的一部分。

For an explanation of mbUnit, see the Hanselman 2006 article. As of this 2019 edit, the other links he includes are dead.

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

In the C# world, NUnit added parameterised testingin a more powerful and flexible way including a way to handle generics as Parameterised Fixtures.

在 C# 世界中,NUnit以更强大和更灵活的方式添加了参数化测试,包括一种将泛型处理为Parameterised Fixtures 的方法

The following test will be executed fifteen times, three times for each value of x, each combined with 5 random doubles from -1.0 to +1.0.

下面的测试将执行 15 次,每个 x 值执行 3 次,每次结合 5 个从 -1.0 到 +1.0 的随机双打。

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

The following test fixture would be instantiated by NUnit three times, passing in each set of arguments to the appropriate constructor. Note that there are three different constructors, matching the data types provided as arguments.

下面的测试装置将被 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());
    }
}

回答by Rob Kennedy

Yes, there's a single parameter. You can make that parameter be arbitrarily complex, though. You could adapting the code from the documentation to use you Rowtype, for example:

是的,只有一个参数。不过,您可以使该参数任意复杂。您可以修改文档中的代码以使用您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>.
};

Then define your parameterized test:

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

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);
  }
}

Finally, instantiate it:

最后,实例化它:

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)
));

回答by kkaja123

An alternative to using a custom structure as the parameter is to use the parameter generator ::testing::Combine(g1, g2, ..., gn). This generator allows you to combine the other parameter generators into a set of parameters with a type std::tuplethat has a template type that matches the types of the values provided.

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

Note that this generator produces the Cartesian productof the values provided. That means that every possible ordered tuple will be created. I believe the original question is asking for a strict array of parameters with the provided values, which this does not support. If you need to have an array of strict parameters, you could use a tuple with the parameter generator ::testing::Values(v1, v2, ..., vN)where each value is a separate tuple.

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

Example:

例子:

#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}));

In the above example, the parameters created for MyTestGroupwould look like the following:

在上面的示例中,创建的参数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}
]

Refer to the GoogleTest documentation for further details.(Accessed on 12/17/2019)

有关详细信息,请参阅 GoogleTest 文档。(访问于 12/17/2019)