私の組織は CppUnit を使用しており、異なるパラメーターを使用して同じテストを実行しようとしています。テスト内でループを実行することは、失敗するとテストが中止されるため、適切なオプションではありません。TestDecorator
andを見てきましたTestCaller
が、どちらも実際には適合していないようです。コードサンプルは役に立ちます。
7 に答える
CppUnit では、テスト ケースを直接パラメーター化することはできないようです (こちらとこちらを参照)。ただし、いくつかのオプションがあります。
使うRepeatedTest
RepeatedTest
組み込みのデコレータをうまく利用できるかもしれません。これにより、テスト ケースを複数回実行できます (ただし、パラメーター化は必要ありません)。
私はこれを自分で使用したことがないことを認めますが、おそらく、RepeatedTest
(クラスの静的変数を使用して?) 実行ごとに異なる入力を選択するゲートキーパー機能を駆動することができます。次に、その値を入力としてテストしたい真の関数を呼び出します。
TestCase
サブクラスを使用する
CppUnit の SourceForge ページの1 人TestCase
は、特定のテストを任意の回数実行する のサブクラスを作成したと主張していますが、その方法はRepeatedTest
クラスが提供するものとは少し異なります。悲しいことに、投稿者はクラスを作成する動機を説明しただけで、ソース コードは提供しませんでした。しかし、詳細について個人に連絡するという申し出がありました。
シンプルなヘルパー関数を使用する
これを行うための最も簡単な (しかし自動化されていない) 方法は、「実際の」関数に渡したいパラメーターを受け取るヘルパー関数を作成し、多数の個別のテスト ケースを用意することです。各テスト ケースは、異なる値でヘルパー関数を呼び出します。
上記の最初の 2 つのオプションのいずれかを選択した場合は、その経験についてお聞きしたいと思います。
class members : public CppUnit::TestFixture
{
int i;
float f;
};
class some_values : public members
{
void setUp()
{
// initialization here
}
};
class different_values : public members
{
void setUp()
{
// different initialization here
}
};
tempalte<class F>
class my_test : public F
{
CPPUNIT_TEST_SUITE(my_test<F>);
CPPUNIT_TEST(foo);
CPPUNIT_TEST_SUITE_END();
foo() {}
};
CPPUNIT_TEST_SUITE_REGISTRATION(my_test<some_values>);
CPPUNIT_TEST_SUITE_REGISTRATION(my_test<different_values>);
それが CppUnit の「推奨される方法」に従ってコーシャと見なされるかどうかはわかりませんが、それが私が現在取っているアプローチです。
Marcin の提案を受けて、パラメーター化された CppUnit テストの定義を支援するいくつかのマクロを実装しました。
このソリューションでは、クラスのヘッダー ファイル内の古いマクロ CPPUNIT_TEST_SUITE および CPPUNIT_TEST_SUITE_END を置き換えるだけです。
CPPUNIT_PARAMETERIZED_TEST_SUITE(<TestSuiteClass>, <ParameterType>);
/*
* put plain old tests here.
*/
CPPUNIT_PARAMETERIZED_TEST_SUITE_END();
実装ファイルで、古い CPPUNIT_TEST_SUITE_REGISTRATION マクロを次のように置き換える必要があります。
CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION ( <TestSuiteClass>, <ParameterType> )
これらのマクロでは、次のメソッドを実装する必要があります。
static std::vector parameters();
void testWithParameter(ParameterType& parameter);
- parameters(): ベクトルにパラメーターを提供します。
- testWithParameter(...): パラメータごとに呼び出されます。これは、パラメーター化されたテストを実装する場所です。
詳細な説明はこちらにあります: http://brain-child.de/engineering/parameterizing-cppunit-tests
ドイツ語版はここにあります: http://brain-child.de/engineering/parametrierbare-tests-cppunit
次のクラス/ヘルパー マクロのペアは、現在のユース ケースで機能します。TestFixture
サブクラスで、1 つのパラメーターを受け入れるメソッドを定義してから、 を使用してテストを追加しますPARAMETERISED_TEST(method_name, argument_type, argument_value)
。
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/ui/text/TestRunner.h>
template <class FixtureT, class ArgT>
class ParameterisedTest : public CppUnit::TestCase {
public:
typedef void (FixtureT::*TestMethod)(ArgT);
ParameterisedTest(std::string name, FixtureT* fix, TestMethod f, ArgT a) :
CppUnit::TestCase(name), fixture(fix), func(f), arg(a) {
}
ParameterisedTest(const ParameterisedTest* other) = delete;
ParameterisedTest& operator=(const ParameterisedTest& other) = delete;
void runTest() {
(fixture->*func)(arg);
}
void setUp() {
fixture->setUp();
}
void tearDown() {
fixture->tearDown();
}
private:
FixtureT* fixture;
TestMethod func;
ArgT arg;
};
#define PARAMETERISED_TEST(Method, ParamT, Param) \
CPPUNIT_TEST_SUITE_ADD_TEST((new ParameterisedTest<TestFixtureType, ParamT>(context.getTestNameFor(#Method #Param), \
context.makeFixture(), \
&TestFixtureType::Method, \
Param)))
class FooTests : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FooTests);
PARAMETERISED_TEST(ParamTest, int, 0);
PARAMETERISED_TEST(ParamTest, int, 1);
PARAMETERISED_TEST(ParamTest, int, 2);
CPPUNIT_TEST_SUITE_END();
public:
void ParamTest(int i) {
CPPUNIT_ASSERT(i > 0);
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(FooTests);
int main( int argc, char **argv)
{
CppUnit::TextUi::TestRunner runner;
CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest( registry.makeTest() );
bool wasSuccessful = runner.run( "", false );
return wasSuccessful;
}
これは非常に古い質問ですが、似たようなことをする必要があり、次の解決策を思いつきました。100% 満足しているわけではありませんが、うまく機能しているようです
テスト メソッドへの一連の入力パラメーターを定義します。たとえば、これらが文字列であるとしましょう。
std::vector<std::string> testParameters = { "string1", "string2" }; size_t testCounter = 0;
呼び出しごとにテスト配列から次のパラメーターを取得する汎用テスター関数を実装します。
void Test::genericTester() { const std::string ¶m = testParameters[testCounter++]; // do something with param }
テストの addTestToSuite() メソッド宣言 (CPPUNIT マクロで非表示) で、CPPUNIT_TEST マクロを使用してメソッドを定義する代わりに (または次に)、次のようなコードを追加します。
CPPUNIT_TEST_SUITE(StatementTest); testCounter = 0; for (size_t i = 0; i < testParameters.size(); i++) { CPPUNIT_TEST_SUITE_ADD_TEST( ( new CPPUNIT_NS::TestCaller<TestFixtureType>( // Here we use the parameter name as the unit test name. // Of course, you can make test parameters more complex, // with test names as explicit fields for example. context.getTestNameFor( testParamaters[i] ), // Here we point to the generic tester function. &TestFixtureType::genericTester, context.makeFixture() ) ) ); } CPPUNIT_TEST_SUITE_END();
このようにして、名前を指定して、genericTester() を各パラメーターに 1 つずつ、複数回登録します。これは私にとって非常にうまくいくようです。
これが誰かに役立つことを願っています。
私は C++ プログラマーではありませんが、単体テストの概念についてはお手伝いできます。
テストケースは、外部パラメーターに依存せずに分離して実行することを意図しています。さらに、テスト ケースの数を、ほとんどのコードをカバーする最小限に抑える必要があります。ただし、一部のテストが同じように見え、いくつかのマイナーなパラメーターが異なるだけの場合があります (そして、私はすでにいくつかを扱いました)。その場合の最善の策は、話しているパラメータを取るフィクスチャを作成し、パラメータごとに1つのテストケースを用意して、フィクスチャを呼び出すことです。一般的な例は次のとおりです。
class MyTestCase
# this is your fixture
def check_special_condition(param)
some
complex
tests
end
# these are your test-cases
def test_1
check_special_condition("value_1")
end
def test_2
check_special_condition("value_2")
end
end
それ以外の場合は、真のテスト ケースを作成していません。なぜなら、テスト ケースを実行している人から多くの知識がなくても再現できるはずだからです。テストへの入力として重要なパラメーターがいくつかあると思います。それでは、それぞれを独自のテストケース内で明示的にしないのはなぜですか? それはまた、数年後にコードを読むプログラマーをガイドする別のドキュメントを書く代わりに、文書化するための最良の方法です.