2016-03-07 2 views
5

CATCH v1.1ビルド14を使用してC++コードの単体テストを行っています。Catch.hppユニットテスト:テストケースを動的に作成するには?

テストの一環として、私のコードでいくつかのモジュールの出力を確認したいと思います。設定された数のモジュールはありません。いつでも多くのモジュールを追加することができます。ただし、各モジュールをテストするコードは同じです。したがって、テストコードをループに入れておくことが理想的だと思います。実際、catch.hppを使用して、各セクションがモジュールに対応するテストケース内でセクションを動的に作成できることを確認しました。私は、例えば、forループでSECTIONマクロを囲むことによってこれを行うことができます。

#include "catch.hpp" 
#include <vector> 
#include <string> 
#include "myHeader.h" 

TEST_CASE("Module testing", "[module]") { 
    myNamespace::myManagerClass manager; 
    std::vector<std::string> modList; 
    size_t n; 

    modList = manager.getModules(); 
    for (n = 0; n < modList.size(); n++) { 
     SECTION(modList[n].c_str()) { 
      REQUIRE(/*insert testing code here*/); 
     } 
    } 
} 

(これは完全な作業例ではありませんが、あなたはアイデアを得る)

をここに私のジレンマです。あるモジュールが故障すると、テストを中止する代わりに他のモジュールをテストし続けるように、モジュールを個別にテストしたいと思います。しかし、CATCHが動作する方法は、単一のREQUIREが失敗した場合、テストケース全体を中止します。このため、個別のセクションだけでなく、モジュールごとに個別のテストケースを作成したいと考えています。 (私は期待通りに)私はTEST_CASEマクロの外に私のforループを入れてみましたが、このコードはコンパイルに失敗します。

#include "catch.hpp" 
#include <vector> 
#include <string> 
#include "myHeader.h" 

myNamespace::myManagerClass manager; 
std::vector<std::string> modList; 
size_t n; 

modList = manager.getModules(); 
for (n = 0; n < modList.size(); n++) { 
    TEST_CASE("Module testing", "[module]") { 
     SECTION(modList[n].c_str()) { 
      REQUIRE(/*insert testing code here*/); 
     } 
    } 
} 

writing my ownmain()ことによってこれを行うことが可能かもしれないが、私が何をどのように見ることができませんそれは正確に。 (私は別のファイルに自分のTEST_CASEコードを維持したい場合、私はmain()に直接私のTEST_CASEコードを入れますか?何を?また、それは私の他、より標準的なテストケースに影響を与えるのでしょうか?)

私もCHECKマクロを使用することができますREQUIREマクロの代わりに、モジュールが失敗したときにテストケースを中止するのを避けるために、反対の問題が発生します。早期に失敗したモジュールでテストを続行しようとします。各モジュールを独自のテストケースに入れれば理想的な動作になります。

CATCHでテストケースを簡単に作成する方法はありますか?もしそうなら、あなたは私にそれを行う方法の例を教えてもらえますか?私はCATCHのドキュメントを読んでオンラインで検索しましたが、これを行う方法の兆候は見つかりませんでした。

答えて

0

キャッチがあるかもしれないようですね私が期待しているプロパティベースのテストに移行することで、テストケースを動的に作成することができます。その間に、私がやったことがここにあります。

私は.cppファイルを作成しました。単一のモジュールの場合はTEST_CASE、モジュール名の場合はグローバル変数です。

module_unit_test.cpp

#include "catch.hpp" 
#include <string> 
#include "myHeader.h" 

extern const std::string g_ModuleName; // global variable: module name 

TEST_CASE("Module testing", "[module]") { 
    myNamespace::myManagerClass manager; 
    myNamespace::myModuleClass *pModule; 
    SECTION(g_ModuleName.c_str()) { 
     pModule = manager.createModule(g_ModuleName.c_str()); 
     REQUIRE(pModule != 0); 
     /*insert more testing code here*/ 
    } 
} 

その後、私はこのテストを実行する実行可能ファイルを作成します(はい、私は私が注意して、そして最後の手段としてそれを使用しています理由である、グローバル変数は悪知っています)コマンドラインで指定された1つのモジュールにインストールします。 (私は以下のCatch::Session().run()をループしてみましたが、Catchは2回以上実行できません)module_test.cpp以下のコードとmodule_unit_test.cppのユニットテストコードのオブジェクトファイルは、実行ファイルを作成するときにリンクされています。

module_test.cpp

module_test_all.cpp:そして

#define CATCH_CONFIG_RUNNER 
#include "catch.hpp" 
#include <string> 
#include <cstdio> 

std::string g_ModuleName; // global variable: module name 

int main(int argc, char* argv[]) { 
    // Make sure the user specified a module name. 
    if (argc < 2) { 
     std::cout << argv[0] << " <module name> <Catch options>" << std::endl; 
     return 1; 
    } 

    size_t n; 
    char* catch_argv[argc-1]; 
    int result; 

    // Modify the input arguments for the Catch Session. 
    // (Remove the module name, which is only used by this program.) 
    catch_argv[0] = argv[0]; 
    for (n = 2; n < argc; n++) { 
     catch_argv[n-1] = argv[n]; 
    } 

    // Set the value of the global variable. 
    g_ModuleName = argv[1]; 

    // Run the test with the modified command line arguments. 
    result = Catch::Session().run(argc-1, catch_argv); 

    return result; 
} 

、Iは、(上記のコードからオブジェクトファイルにリンクされていない)別の実行可能でないループ

#include <cstdlib> 
#include <vector> 
#include <string> 
#include "myHeader.h" 

int main(int argc, char* argv[]) { 
    std::string commandStr; 
    int result, status = 0; 
    myNamespace::myManagerClass manager; 
    std::vector<std::string> modList; 
    size_t m, n; 

    // Scan for modules. 
    modList = manager.getModules(); 

    // Loop through the module list. 
    for (n = 0; n < modList.size(); n++) { 
     // Build the command line. 
     commandStr = "module_test " + modList[n]; 
     for (m = 1; m < argc; m++) { 
      commandStr += " "; 
      commandStr += argv[m]; 
     } 

     // Do a system call to the first executable. 
     result = system(commandStr.c_str()); 

     // If a test fails, I keep track of the status but continue 
     // looping so all the modules get tested. 
     status = status ? status : result; 
    } 

    return status; 
} 

はいそれは醜いですが、私はそれが動作することを確認しました。

1

あり、あなたが探しているものを達成するための方法があるが、私はあなたがこのことについて間違った道を進んでいることを確認したい: -

ユニットテストは、各ユニットをテストすることを意図しているあなた、すなわちコンポーネントを記述し、そのコンポーネントの正しい動作を検証するテスト。あるコンポーネントを後で何らかの方法で変更する場合は、対応するテストを更新します。

すべてのコンポーネントのすべてのテストを同じファイルに集約すると、動作が異なるユニットを分離するのがずっと難しくなります。

あなたは、それは基本的にすべてのコンポーネントで同じですので、コンポーネントのテストを考慮したい場合は、次のいずれかを行うことができます:

1。あなたがテストしたい部品のタイプ名を#defineした後、その中のすべてのテストを持つヘッダファイルをインクルードすることができます

別のヘッダファイルに共通のテストを抽出します。

// CommonTests.t.h 
#include "catch.hpp" 
TEST_CASE("Object Can be instantiated", "[ctor]") 
{ 
    REQUIRE_NOTHROW(COMPONENT component); 
} 

// SimpleComponent.t.cpp 
#define COMPONENT SimpleComponent 
#include "CommonTests.t.h" 

これは、簡単に行うことができますが、テストランナーを実行すると、すべてのテストまたはタグだけを実行できるように、テストを(名前で)繰り返すことになります。

これを解決するには、コンポーネント名を文字列化し、それをテストケース名に追加または追加します。

** 2.コールコンポーネントをパラメータ化することにより、共通のテストが**

別のファイルにあなたの共通のテストを入れて、直接、共通のテストメソッドを呼び出す:

// CommonTests.t.h 
void RunCommonTests(ComponentInterface& itf); 

// CommonTests.t.cpp 
void RunCommonTests(ComponentInterface& itf) 
{ 
    REQUIRE(itf.answerToLifeUniverseAndEverything() == 42); 
} 

// SimpleComponent.t.cpp 
#include "SimpleComponent.h" 
#include "CommonTest.t.h" 
#include "catch.hpp" 

TEST_CASE("SimpleComponent is default-constructible", "[ctor]") 
{ 
    REQUIRE_NOTHROW(SimpleComponent sc); 
} 

TEST_CASE("SimpleComponent behaves like common components", "[common]") 
{ 
    SimpleComponent sc; 
    RunCommonTests(sc); 
} 
+0

私はあなたの最初の提案を使用しています。理想的には、実行時に動的にモジュールを検出し、それぞれに対してテストケースを生成したいと思いますが、Catchではそれが不可能だと考え始めています。背景:モジュールは、私が定義した抽象基本クラスから派生クラスを定義します。それらは異なる出力を生成するが、出力をチェックする手順は同じである。他の開発者は独自のモジュールを追加するので、独自のモジュールでコンプライアンスをチェックするためにテスト機能を使用できるといいでしょう。たぶん私は間違っているが、私はより良い方法を見ていない。 –

+1

Googleグループの詳細については、https://groups.google.com/forum/#!searchin/catch-forum/section/catch-forum/mRBKqtTrITU/FoHEoMn3SN8Jをご覧ください – JBRWilkinson

関連する問題