web-dev-qa-db-ja.com

ユニットテストC ++。プライベートメンバーをテストする方法は?

C++アプリケーションの単体テストを作成したいと思います。

クラスのプライベートメンバーをテストするための正しい形式は何ですか?プライベートメンバーのテスト、派生クラスの使用、またはその他のトリックを行うフレンドクラスを作成しますか?

テストAPIはどの手法を使用しますか?

38
Daniel Saad

通常、質問のコメントで説明されているように、パブリックインターフェイスのみをテストします。

ただし、プライベートメソッドまたは保護されたメソッドをテストすると役立つ場合があります。たとえば、実装には、ユーザーからは見えない非自明な複雑さがあり、非公開メンバーへのアクセスでより正確にテストできます。多くの場合、その複雑さを取り除く方法を理解するか、関連する部分を常に公開する必要はありませんが、公開する方法を理解する方が適切です。

非公開メンバーへの単体テストアクセスを許可する1つの方法は、 friend 構文を使用することです。

37
Mr Fooz

この質問に答えることは、他の多くのトピックに影響を与えます。 CleanCode、TDD、およびその他の宗教のほかに:

プライベートメンバーにアクセスする方法はいくつかあります。いずれにしても、テストしたコードを無効にする必要があります!これは、C++の両方の解析レベル(プリプロセッサと言語自体)で可能です。

すべてをパブリックに定義

プリプロセッサを使用すると、カプセル化を解除できます。

_#define private public
#define protected public
#define class struct
_

欠点は、提供されたコードのクラスがテストと同じではない! 9.2.13章のC++標準には次のように書かれています。

異なるアクセス制御を持つ非静的データメンバーの割り当て順序は指定されていません。

これは、コンパイラーがテストのためにメンバー変数と仮想関数を並べ替える権利を持っていることを意味します。バッファオーバーフローが発生しない場合、クラスに害を及ぼさないという苦労がありますが、それは、配信するコードと同じコードをテストしないことを意味します。つまり、コードで初期化され、privateに定義されていないpublicでコンパイルされたオブジェクトのメンバーにアクセスすると、メンバーのオフセットが異なる可能性があります。

友達

このメソッドは、テストクラスまたはテスト関数とフレンドリングするために、テストされたクラスを変更する必要があります。 gtest(FRIEND_TEST(..);)などの一部のテストフレームワークには、プライベートなものにアクセスするこの方法をサポートする特別な機能があります。

_class X
{
private:
    friend class Test_X;
};
_

テストのためにのみクラスを開き、世界を開きませんが、配信されるコードを変更する必要があります。私の意見では、これは悪いことです。なぜなら、テストはテストされたコードを変更してはならないからです。さらに不利な点として、提供されたコードの他のクラスに、テストクラスのように名前を付けることにより、クラスに侵入する可能性が与えられます(これは、C++標準のODRルールも害します)。

テストのために保護され、クラスから派生するプライベートなものを宣言する

非常にエレガントな方法ではなく、非常に邪魔になりませんが、次のようにも機能します:

_class X
{
protected:
    int myPrivate;
};

class Test_X: public X
{
    // Now you can access the myPrivate member.
};
_

マクロを使用する他の方法

動作しますが、最初の方法と同様に、標準適合性に関して同じ欠点があります。例えば。:

_class X
{
#ifndef UNITTEST
private:
#endif
};
_

最後の両方の方法は最初の2つの方法に代わるものではないと思います。最初の方法よりも利点はありませんが、テストされたコードにはより邪魔になるからです。最初の方法は非常にリスクが高いため、友好的なアプローチを使用できます。


決してテスト、プライベート、物事の議論に関するいくつかの言葉。単体テストの利点の1つは、コードの設計を改善する必要があるポイントに非常に早く到達することです。これは、ユニットテストの欠点の1つである場合もあります。オブジェクトの向きが必要以上に複雑になることがあります。特に、現実世界のオブジェクトと同じ方法でクラスを設計するルールに従う場合。

その後、コードを時々theいものに変更する必要があります。ユニットテストのアプローチでは、そうする必要があるからです。物理プロセスを制御するために使用される複雑なフレームワークでの作業は、その一例です。多くの場合、プロセスの一部はすでに非常に複雑であるため、物理プロセスにコードをマッピングする必要があります。そのプロセスの依存関係リストが非常に長くなる場合があります。これは、プライベートメンバーのテストがニースになった1つの瞬間です。各アプローチの長所と短所をトレードオフする必要があります。

クラスは時々複雑になっています!次に、それらを分割するか、そのまま使用するかを決定する必要があります。場合によっては、2番目の決定がより理にかなっています。最終的には、常にどの目標を達成したいか(たとえば、完璧な設計、迅速な組み込み時間、低開発コストなど)が問題になります。


私の意見

プライベートメンバーにアクセスするための私の決定プロセスは次のようになります。

  1. プライベートメンバー自身をテストする必要がありますか? (多くの場合、これは必要なテストの総数を減らします)
  2. はいの場合、クラスをリファクタリングする設計上の利点はありますか?
  3. いいえの場合、クラスのテストの友達になります(選択肢がないため、これを使用します)。

友好的なアプローチは好きではありません。テストされたコードが変更されるためです。しかし、(最初のアプローチで可能な限り)提供されたものとは異なる可能性のある何かをテストするリスクは、よりクリーンなコードを正当化しません。

ところで:公開インターフェースのみをテストすることも流な問題です。私の経験では、プライベート実装と同じくらい頻繁に変更されるからです。したがって、公開メンバーのテストを減らすメリットはありません。

22
Stefan Weiser

ゴールデンソリューションを自分で見つけたわけではありませんが、friendを使用してプライベートメンバーをテストできます(テストフレームワークでメソッドの名前がわかれば)。以下を使用して、プライベートメンバーをGoogleテストでテストします。これは非常にうまく機能しますが、ハッキングであり、本番コードでは使用しないことに注意してください。

テストするコードのヘッダー(stylesheet.h)には、次のものがあります。

#ifndef TEST_FRIENDS
#define TEST_FRIENDS
#endif

class Stylesheet {
TEST_FRIENDS;
public:
    // ...
private:
    // ...
};

そして、テストでは:

#include <gtest/gtest.h>

#define TEST_FRIENDS \
    friend class StylesheetTest_ParseSingleClause_Test; \
    friend class StylesheetTest_ParseMultipleClauses_Test;

#include "stylesheet.h"

TEST(StylesheetTest, ParseSingleClause) {
    // can use private members of class Stylesheet here.
}

プライベートメンバーにアクセスする新しいテストを追加する場合は、常にTEST_FRIENDSに新しい行を追加します。この手法の利点は、テストしていないときには効果がない#defineをいくつか追加するだけなので、テストしたコードでは目立たないことです。欠点は、テストで少し冗長になることです。

なぜあなたがこれをしたいのかということについて一言。理想的には、明確に定義された責任を持つ小さなクラスがあり、そのクラスには簡単にテスト可能なインターフェースがあります。ただし、実際にはそれは必ずしも簡単ではありません。ライブラリを作成している場合、privateおよびpublicとは、必要なものではなく、ライブラリのコンシューマが使用できるようにするもの(パブリックAPI)によって決まります。テストの有無。変更する可能性が非常に低く、テストする必要のある不変条件を持つことができますが、APIのコンシューマーには関係ありません。その場合、APIのブラックボックステストでは十分ではありません。また、バグに遭遇し、リグレッションを防ぐための追加のテストを作成する場合は、privateをテストする必要があります。

19
jdm

プライベートメンバーをテストしたいという願望は、一般的にクラスから抜け出すのに苦労しているクラスがあることを示しています。クラスのすべての機能は、そのパブリックメソッドを通じて実行可能である必要があります。一般にアクセスできない機能は実際には存在しません。

プライベートメソッドがブリキで言うことを実行することをテストする必要があることを認識するためのアプローチがいくつかあります。友だちクラスはこれらの最悪です。彼らは、テストを、テスト中のクラスの実装に、一見壊れやすい方法で結び付けます。やや良い依存関係の注入:パブリックインターフェイスを介してプライベートメソッドのテストを許可するために、テストがモックアップバージョンを提供できるプライベートメソッドの依存関係クラス属性を作成します。最善の方法は、プライベートメソッドがそのパブリックインターフェイスとして持つ動作をカプセル化するクラスを抽出し、通常どおりに新しいクラスをテストすることです。

詳細については、 Clean Code を参照してください。

4
darch

プライベートメソッドのテストの適切性に関するコメントにもかかわらず、本当に必要な場合を考えてみてください。たとえば、より適切なものにリファクタリングする前にレガシーコードを操作する場合などです。私が使用したパターンは次のとおりです。

// In testable.hpp:
#if defined UNIT_TESTING
#   define ACCESSIBLE_FROM_TESTS : public
#   define CONCRETE virtual
#else
#   define ACCESSIBLE_FROM_TESTS
#   define CONCRETE
#endif

次に、コード内で:

#include "testable.hpp"

class MyClass {
...
private ACCESSIBLE_FROM_TESTS:
    int someTestablePrivateMethod(int param);

private:
    // Stuff we don't want the unit tests to see...
    int someNonTestablePrivateMethod();

    class Impl;
    boost::scoped_ptr<Impl> _impl;
}

テストの友達を定義するよりも良いですか?それは代替案よりも冗長ではなく、ヘッダー内で何が起こっているのか明確です。どちらのソリューションもセキュリティとは関係ありません。メソッドまたはメンバーが本当に心配な場合は、他の保護を使用して、不透明な実装内にこれらを隠す必要があります。

3
mbells

場合によっては、プライベートメソッドをテストする必要があります。 FRIEND_TESTをクラスに追加することにより、テストを実行できます。

// Production code
// prod.h

#include "gtest/gtest_prod.h"
...   

class ProdCode 
    {
     private:
      FRIEND_TEST(ProdTest, IsFooReturnZero);
      int Foo(void* x);
    };

//Test.cpp
// TestCode
...
TEST(ProdTest, IsFooReturnZero) 
{
  ProdCode ProdObj;
  EXPECT_EQ(0, ProdObj.Foo(NULL)); //Testing private member function Foo()

}
1
Syam Sanal

C++には#defineを使用した簡単なソリューションがあります。次のように、「ClassUnderTest」のインクルードをラップするだけです。

#define protected public
 #define private   public
    #include <ClassUnderTest.hpp>
 #undef protected
#undef private

[クレジットはこの記事とRonFoxに行きます] [1]

0
Langley