3

現在、静的関数のみを持つ別のクラスを使用するクラスに取り組んでいます。

クラスをテストするまで、すべてがうまくいきました。

問題の簡単なコード例を次に示します。

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

クラス A が正しく動作している (そして単体テストでテストされている) と仮定して、クラス B のrunSomething関数を確認したいと思います。

私の最初のオプションは、内部クラス (このサンプルでは - クラス A) のモックを作成することですが、その場合、静的関数しかないため、A から継承するものは何もありません。

2 つ目のオプションは、A クラスの呼び出しを B 内のプライベート関数にカプセル化して、戻り値を制御できるようにすることです (ただし、このオプションを選択すると、処理が少し複雑になります)。

私の質問は次のとおりです。静的クラス/関数に依存する C++ クラスをテストするための、現在のオプションよりも優れた方法はありますか?

前もって感謝します、

タル。

4

6 に答える 6

4

静的関数(私の場合は外部依存関係)への参照を新しいプライベート関数にリファクタリングし、テストされたスタブでそれらを上書きすることで、同様の状況で単体テストに成功したので、このアプローチをお勧めします

リファクタリングされた関数がプライベートのままである場合、それらは設計の複雑さに大きな影響を与えないはずであり、コードの可読性に悪影響を与えないように十分に小さい必要があります。

于 2010-11-01T23:19:11.930 に答える
1

モノリシックなテスト スイートを使用していない場合は、簡単です。A.cpp にクラス A があり、B.cpp にクラス B があり、B のテストが B_test.cpp にあるとします。

A_mock.cpp というファイルを作成します。

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

次に、B_test ファイルをコンパイルするときに、 Ao ではなく A_mock.o にリンクします。

g++ -Wall B_test.cpp B.cpp A_mock.cpp
于 2010-11-02T00:02:55.000 に答える
0

関数へのポインターをクラス A のコンストラクターに渡すことができます。次に、テストのために、必要なことを実行できるモック関数へのポインターを渡すことができます。

于 2010-11-02T08:39:53.737 に答える
0

なぜ静的関数なのですか?静的にしないことをお勧めします。

次に、AInterface という名前のクラス A (C++ では、これは純粋仮想関数ヘッダーのみを持つクラスを意味します) のインターフェイスを作成できます。クラス A は AInterface を実装 (継承) し、この仮想関数を実装します。

次に、このインターフェイスへのポインターをクラス B のコンストラクターに渡し、m_A という名前のメンバー変数に格納します。次に、テストで、AInterface を実装する MockClassA を作成します。MockClassA をクラス B コンストラクターに渡し、m_A を入力に設定します。

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

テストコード

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}
于 2017-05-26T17:25:44.843 に答える
-1

「男、ユニットテストをやりすぎている人もいるよ!」と私は言います。

2つのクラスを1つのユニットとしてテストするだけです。とにかく、クラスAはクラスBにハードコードされています。

于 2010-11-01T23:19:18.360 に答える
-1

テンプレートを介してクラスを取得し、そのインスタンス化 ( B<A>) を明示的にエクスポートして、投稿されたようにすべてインライン化されていなかった場合にリンカーの問題を回避する必要があります。このようにして、必要に応じてテストの目的で他のクラスを挿入できますが、とにかく良い方法です。また、あなたの例が Java に非常によく似ている理由についても興味があります。記載されているように、実際C++ であると判断する前に、約 5 回読む必要がありました。

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

A から継承することはできませんが、モック クラスを A に置き換えることができるようになりました。実際、これは別の方法で拡張可能です-CRTP。

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

テンプレートの素晴らしさは、私を驚かせてやみません。

于 2010-11-01T23:41:33.500 に答える