24

C++/CLI で記述された次の mbUnit テストの複雑さに理想的に一致する、異なるデータ型の複数のパラメータで値パラメータ化されたテストを使用できる C++ Google テストを記述したいと思います。

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)
{
...
}

ヘルプには、値パラメーター化されたテストを使用すると、テストを 1 回だけ記述して、任意の数のパラメーター値で簡単にインスタンス化して実行できると書かれています。しかし、それはテストケースの数を指していると確信しています。

データ型を変えなくても、パラメータ化されたテストは1 つのパラメータしかとれないように思えますか?

2019年更新

この質問について ping を受け取ったので追加しました。Row表示されている属性は mbUnit の一部です。

mbUnit の説明については、Hanselman 2006 の記事を参照してください。この2019年の編集の時点で、彼が含む他のリンクは無効になっています.

C# の世界では、NUnit はジェネリックをParameterized Fixturesとして処理する方法など、より強力で柔軟な方法でパラメーター化されたテストを追加しました。

次のテストは、x の値ごとに 3 回、合計 15 回実行され、それぞれが -1.0 から +1.0 までの 5 つのランダムな double と組み合わされます。

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

次のテスト フィクスチャは、NUnit によって 3 回インスタンス化され、引数の各セットを適切なコンストラクターに渡します。引数として提供されるデータ型に一致する 3 つの異なるコンストラクターがあることに注意してください。

[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());
    }
}
4

2 に答える 2

33

はい、単一のパラメーターがあります。ただし、そのパラメーターを任意に複雑にすることはできます。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)
));
于 2011-06-06T19:11:47.687 に答える
8

カスタム構造体をパラメーターとして使用する代わりに、パラメーター ジェネレーターを使用することもできます::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アクセス)

于 2019-12-17T15:36:46.087 に答える