.net - 使い方 - どのように単体テストのプライベートメソッドをテストしますか?




xunit private method (20)

.netを使用している場合は、 InternalsVisibleToAttributeを使用する必要があります。

私はいくつかのパブリックメソッドとプライベートメソッドを持つクラスライブラリを構築しています。 プライベートメソッドを単体テストすることができるようにしたい(主に開発中ですが、将来のリファクタリングにも役立ちます)。

これを行う正しい方法は何ですか?


CodeProjectには、プライベートメソッドのテストの長所と短所を簡単に説明する記事があります。 次に、プライベートメソッドにアクセスするためのリフレクションコードを提供します(Marcusのコードと同様です)。サンプルで見つかった唯一の問題は、コードがオーバーロードされたメソッドを考慮しないことです。

ここで記事を見つけることができます:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx


MbUnitはReflectorと呼ばれる素晴らしいラッパーを手に入れました。

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

プロパティから値を設定および取得することもできます

dogReflector.GetProperty("Age");

"テストプライベート"に関して、私は完璧な世界でそれに同意します。 私的な単体テストを行うことには意味がありません。 しかし現実の世界では、コードをリファクタリングするのではなく、私的なテストを書くことになりかねません。


1)レガシーコードをお持ちの場合、プライベートメソッドをテストする唯一の方法は、リフレクションによるものです。

2)新しいコードの場合は、次のオプションがあります。

  • 反射を使用する(複雑にする)
  • 同じクラスに単体テストを書く(テストコードを入れて生産コードを醜いものにする)
  • リファクタリングし、ある種のutilクラスでメソッドをpublicにする
  • @VisibleForTestingアノテーションを使用してプライベートを削除する

私は、最もシンプルで最も複雑でない注釈方法を好む。唯一の問題は、私が大きな関心事ではないと思う可視性を向上させたことです。私たちは常にインタフェースを記述する必要があります。インタフェースMyServiceと実装MyServiceImplがあれば、対応するテストクラスMyServiceTest(テストインタフェースメソッド)とMyServiceImplTest(テストプライベートメソッド)を持つことができます。プライベートメソッドの可視性が向上したにもかかわらず、すべてのクライアントがインタフェースを使用する必要があります。


ここでは、まずメソッドシグニチャの例を示します。

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

ここにテストがあります:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}

ここでは、プライベートメソッドをテストする任意のクラスで使用できる明確なコード例を作成したいと思います。

あなたのテストケースのクラスでは、これらのメソッドをインクルードし、指定されたとおりに使用します。

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ this - > _ callMethod( '_ someFunctionName'、array(param1、param2、param3));

元の私的関数に現れる順序でパラメータを発行するだけです


それらをinternalで宣言し、次にInternalsVisibleToAttributeを使用して、ユニットテストアセンブリがそれらを参照できるようにします。


まず、コードのプライベートメソッドをテストするべきではありません。 クラスの公共のものである「パブリックインターフェイス」やAPIをテストする必要があります。 APIは、外部の呼び出し元に公開するすべてのパブリックメソッドです。

その理由は、クラスのプライベートメソッドと内部のテストを開始すると、クラスの実装(プライベートなもの)をテストに結合することになります。 つまり、実装の詳細を変更する場合は、テストを変更する必要があります。

この理由から、InternalsVisibleToAtrributeを使用しないでください。

ここにIan Cooperの素晴らしい話があります: Ian Cooper:TDD、どこが間違っていたのですか?


また、debug-Modeでビルドしている間は、publicまたはinternal(InternalsVisibleToAttributeを使用)として宣言することもできます。

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

それはコードを膨らませますがprivate、リリースビルドになります。


まれに私は私的関数をテストしたいと思っていましたが、私は通常それらを保護するように変更しました。私はpublicラッパー関数を使ってサブクラスを作成しました。

クラス:

...

protected void APrivateFunction()
{
    ...
}

...

テストのサブクラス:

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...

プライベートタイプ、内部およびプライベートメンバーはなんらかの理由でそうであり、しばしばそれらを直接混乱させたくありません。 また、そうしたアセンブリを作成した人が私的/内部実装をそのまま維持する保証はないので、後で破る可能性があります。

しかし、時々、コンパイルされたアセンブリや第三者のアセンブリをいくつかハッキングしたり探検したりするとき、私は自分自身をプライベートクラスまたはプライベートクラスまたは内部コンストラクタを持つクラスに初期化したいと思ってしまいました。 あるいは、私が変更できない、あらかじめコンパイルされたレガシーライブラリを扱うときに、私は私的な方法に対していくつかのテストを書くことになります。

したがって、AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html生まれました。これは、C#4.0の動的な機能とリフレクションを使用して簡単に作業を行えるクイックラッパークラスです。

のような内部/プライベートタイプを作成することができます

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();


プライベートメソッドを単体テストする場合は、何かが間違っている可能性があります。 単体テストは(一般的に言えば)クラスのインタフェースをテストすることを意味します。つまり、public(およびprotected)メソッドを意味します。 もちろん、メソッドをパブリックにするだけで、これに対する解決策を "ハック"することはできますが、次の点も考慮する必要があります。

  1. テストしたいメソッドが実際にテストする価値がある場合は、それを自分のクラスに移動する価値があります。
  2. プライベートメソッドを呼び出すパブリックメソッドにさらにテストを追加し、プライベートメソッドの機能をテストします。 (解説者が示したように、これらのプライベートメソッドの機能が実際にパブリックインターフェイスの一部である場合にのみ、これを行う必要があります。実際にユーザーから隠された機能(ユニットテスト)を実際に実行する場合は、

場合によっては、私的宣言をテストすることもできます。 基本的に、コンパイラーはパブリックメソッドを1つしか持たない:Compile(string outputFileName、params string [] sourceSFileNames)。 私は、それぞれの "隠された"宣言をテストすることなく、そのようなメソッドをテストすることは難しいと理解していると思います!

だからこそ私たちはVisual T#を作成しました。これはテストを簡単にするためです。 これは無料の.NETプログラミング言語です(C#v2.0互換)。

'.-'演算子を追加しました。 それはちょうど 'のように振る舞います。 テストされたプロジェクトで何も変更せずに、テストから隠された宣言にアクセスすることができる点を除いて、

私たちのウェブサイトをご覧ください: 無料で downloadてください


私は "あなたは外部インターフェースをテストすることに興味がある"という考え方に同意しない。 車修理店では車輪が回転するかどうかを調べるだけのテストが必要だと言っているような感じです。 はい、最終的に私は外部の動作に興味がありますが、私は私自身の個人的な内部テストがもう少し具体的で、そのポイントに似ています。 はい、私がリファクタリングしている場合は、いくつかのテストを変更する必要があるかもしれませんが、大規模なリファクタリングでなければ、変更する必要があるだけで、他の(変更されていない)内部テストがまだ機能しているということは、リファクタリングが成功しました。

パブリックインターフェイスのみを使用してすべての内部ケースをカバーすることができます。理論的には、パブリックインターフェイスを使用して、すべての内部メソッド(または少なくともすべてのもの)を完全にテストすることは可能ですが、これとパブリックインターフェイスを介して実行されているテストケースと、テストするように設計されたソリューションの内部部分との間の接続は、識別するのが困難または不可能な場合があります。 つまり、内部の機械が適切に動作していることを保証する個々のテストは、リファクタリングに伴うマイナーなテストの変更に価値があります。少なくともそれは私の経験でした。 すべてのリファクタリングのためにテストに大きな変更を加えなければならない場合は、これは意味をなさないかもしれませんが、その場合は、デザインを完全に再考する必要があります。 優れた設計は、大規模な再設計なしでほとんどの変更を可能にするほど柔軟でなければなりません。


私は、コンパイル・ディレクティブを使用しない傾向があります。 本当に必要な場合には、それを軽減する1つの方法は、それらを部分クラスに入れて、実動バージョンを作るときにそのビルドにその.csファイルを無視させることです。


Visual Studio 2008からプライベートメソッドのテストメソッドを生成できます。プライベートメソッドの単体テストを作成すると、Test Referencesフォルダがテストプロジェクトに追加され、アクセサがそのフォルダに追加されます。アクセサはユニットテストメソッドのロジックでも参照されます。このアクセサを使用すると、単体テストで、テスト中のコード内のプライベートメソッドを呼び出すことができます。詳細はこちらをご覧ください

http://msdn.microsoft.com/en-us/library/bb385974.aspx


ここでプライベートメソッドの単体テストについての良いarticleです。しかし、何が良いのか分かりません。アプリケーションをテスト用に特別に設計するようにしてください(テスト専用のテストを作成するのと同じです)。あるいは、テスト用のリフレクションを使用します。私たちのほとんどが第二の方法を選ぶだろうと確信しています。


また、InternalsVisibleToAtrributeにはアセンブリに強い名前を付けるという要件があることに注意してください。これは以前はその要件を満たしていなかったソリューションで作業している場合には独自の問題のセットを作成します。私はアクセサを使用してプライベートメソッドをテストします。この例については、この質問を参照してください。


私はPrivateObjectクラスを使用します。しかし、前述のようにプライベートメソッドのテストを避けるためには、

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);




private