プライベートメソッドをどのようにユニットテストしますか?


479

いくつかのパブリックメソッドとプライベートメソッドを持つクラスライブラリを構築しています。私はプライベートメソッドをユニットテストできるようにしたいと思います(ほとんどは開発中ですが、将来のリファクタリングにも役立つ可能性があります)。

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


3
私は何かを見逃しているかもしれません、またはおそらくこの質問がそうであるだけかもしれません... pre-historicインターネット時代の観点から、プライベートメソッドのユニットテストは簡単で簡単です、必要なときにVisual Studioが必要なアクセサクラスを生成し、テストロジックにスニペットを事前に入力すると、単純な機能テストに必要なものに近くなります。たとえばを参照してください。msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspx
mjv 2011

3
これは、stackoverflow.com / questions / 34571 / …のほぼ複製のようです。
Raedwald、2011年

質問者はビジュアルスタジオを使用していない可能性があります
Dave


回答:


122

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


86
ああ。これは、リリースされたアセンブリにコンパイルされます。
ジェイ

14
@Jay- リリースコードに適用されないようにするために#if DEBUGInternalsVisibleTo属性の周りで使用できませんでしたか?
mpontillo 2010

20
@マイク、できますが、ユニットコードはリリースコードではなくデバッグコードでのみ実行できます。リリースコードが最適化されているため、動作やタイミングが異なる場合があります。マルチスレッドコードでは、これはユニットテストが競合状態を適切に検出しないことを意味します。以下の@AmazedSaintの提案によるリフレクションを使用するか、組み込みのPrivateObject / PrivateTypeを使用する方がはるかに優れています。これにより、テストハーネスが完全に信頼されて実行されている(MSTestがローカルで実行する)と想定して、リリースビルドのプライベートを確認できます
Jay

121
何が欠けていますか?プライベートメソッドのテストという特定の質問に実際に答えないのに、なぜこれが受け入れられる答えになるのでしょうか。InternalsVisibleToは、内部としてマークされたメソッドのみを公開し、OPによって要求されたプライベートとしてマークされたメソッド(および私がここに着陸した理由)を公開しません。セブンが答えたようにPrivateObjectを使い続ける必要があると思いますか?
Darren Lewis、

6
@Jay私はこれが少し遅れて来ることを知っていますが、1つのオプションは、Mikeが提案するような#if RELEASE_TEST周りのものを使用InternalsVisibleToし、を定義するリリースビルド構成のコピーを作成することRELEASE_TESTです。最適化を使用してリリースコードをテストできますが、実際にリリース用にビルドすると、テストは省略されます。
Shaz 2013

349

プライベートメソッドを単体テストする場合は、何かが間違っている可能性があります。ユニットテストは(一般的に言えば)クラスのインターフェースをテストすることを意味しており、それはそのパブリック(そして保護された)メソッドを意味します。もちろん、これに対するソリューションを「ハック」することもできます(たとえメソッドを公開するだけでも)が、次のことも検討する必要があります。

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

38
オプション2を使用すると、単体テストで関数の基本的な実装に関する知識が必要になります。私はそうするのが好きではありません。私は通常、単体テストは実装について何も想定せずに関数をテストするべきだと思います。
Herms

15
実装のテストの短所は、実装に変更を加えると、テストが壊れやすくなることです。また、リファクタリングはTDDでテストを記述するのと同じくらい重要なので、これは望ましくありません。
JtR 08年

30
まあ、実装を変更するテスト壊れるはずです。TDDは、最初にテストを変更することを意味します。
sleske 2009

39
@sleske-私はまったく同意しません。機能が変更されていない場合、テストは実装ではなく実際に動作/状態をテストする必要があるため、テストが中断する理由はありません。これは、jtrがテストを脆弱にするという意味でした。理想的な世界では、コードをリファクタリングしてテストに合格し、リファクタリングによってシステムの機能が変更されていないことを確認できます。
アルコンハ2009年

26
素朴な謝罪(まだテストの経験が少ない)ですが、単体テストですべてのコードモジュールを単体でテストするという考えではありませんか?プライベートメソッドをこのアイデアから除外する必要がある理由がよくわかりません。
OMill 2011年

118

プライベートメソッドのテストは役に立たない場合があります。ただし、テストメソッドからプライベートメソッドを呼び出すことも好きです。ほとんどの場合、テストデータ生成のコードの重複を防ぐために...

Microsoftはこれのために2つのメカニズムを提供します。

アクセサ

  • クラス定義のソースコードに移動する
  • クラスの名前を右クリック
  • 「Create Private Accessor」を選択します
  • アクセサーを作成するプロジェクトを選択します=> foo_accessorという名前の新しいクラスが作成されます。このクラスは、コンパイル時に動的に生成され、すべてのメンバーがパブリックで利用できるようになります。

ただし、元のクラスのインターフェースの変更に関しては、メカニズムが少し扱いに​​くい場合があります。したがって、ほとんどの場合、これを使用することは避けます。

PrivateObjectクラス もう1つの方法は、Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObjectを使用することです。

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

2
プライベート静的メソッドをどのように呼び出しますか?
StuperUser 2012年

18
プライベートアクセサーは、Visual Studio 2012では非推奨です
ライアンゲイツ

3
プライベートメソッドをテストするアクセサーメソッドは、VS 2011以降では非推奨です。blogs.msdn.com/b/visualstudioalm/archive/2012/03/08/...
Sanjit Misraの

1
ここのマイクロソフトのウェブサイトにあるドキュメントを読んで、非推奨となっているPrivateObjectクラスについての言及はありません。MSVS 2013を使用していますが、期待どおりに動作します。
stackunderflow 2013年

3
@RyanGates参照したサイトの最初のソリューションでは、プライベートメンバーにアクセスするためのソリューションは、「PrivateObjectクラスを使用して、コード内の内部およびプライベートAPIへのアクセスを支援します。これは、Microsoft.VisualStudio.QualityTools.UnitTestFrameworkにあります。 dllアセンブリ。」
stackunderflow 2013年

78

「外部インターフェースのテストのみに関心を持つべき」という哲学には同意しません。自動車修理工場は、車輪が回転するかどうかを確認するためのテストだけを行うべきだと言うのと少し似ています。はい、最終的には外部の振る舞いに興味がありますが、私自身のプライベートな内部テストをもう少し具体的かつ的確にしたいです。はい、リファクタリングする場合、いくつかのテストを変更する必要があるかもしれませんが、大規模なリファクタリングでない限り、変更する必要があるのはわずかであり、他の(変更されていない)内部テストが引き続き機能するという事実は、リファクタリングは成功しています。

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


20
私はまだあなたに同意していません。各コンポーネントをブラックボックスとして扱うと、モジュールを問題なくスワップイン/スワップアウトできます。あなたが持っている場合はFooService関係していることをX、あなたが気にする必要があり、すべてはそれが本当にやるんですX要求されたとき。それがどのように行われるかは重要ではありません。(おそらくない)インターフェースで識別できない問題がクラスにある場合でも、それは有効ですFooService。それが問題だ場合であるインターフェースを通して見える、公共のメンバーのテストでは、それを検出する必要があります。重要なのは、ホイールが正しく回転する限り、ホイールとして使用できるということです。
基本的な

5
一般的なアプローチの1つは、内部ロジックが複雑でユニットテストが必要だと感じる場合は、ユニットテストできるパブリックインターフェイスを持つある種のヘルパークラスに抽出する必要があるというものです。そうすれば、「親」クラスはこのヘルパーを簡単に利用でき、誰でも適切にユニットテストを実行できます。
ミール

9
@基本:この答えの完全に間違ったロジック。プライベートメソッドが必要な古典的なケースは、パブリックメソッドで再利用するコードが必要な場合です。このコードをPrivMethodに配置します。このメソッドはpublicに公開されるべきではありませんが、PrivMethodに依存するpublicメソッドが本当にそれに依存できることを確認するためのテストが必要です。
ディマ2013

6
@Dima確かにに問題があるPrivMethod場合、PubMethodどの呼び出しPrivMethodでそれを公開する必要があるかをテストしますか?あなたを変更したときに何が起こるSimpleSmtpServiceGmailService?突然、アプリケーションが設計どおりに正常に動作する場合でも、プライベートテストが存在しないか、動作が異なり、失敗する可能性があるコードを指しています。両方のメール送信者に適用される複雑な処理がある場合、おそらくEmailProcessor両方で使用でき、個別にテストできるものにすべきでしょうか?
基本的な

2
@miltonbさまざまな開発スタイルからこれを見ているかもしれません。内部のWRT、私はそれらを単体テストする傾向はありません。問題がある場合(インターフェーステストで特定)、デバッガーを接続することで簡単に追跡できるか、クラスが複雑すぎて分割する必要があります(新しいクラスユニットのパブリックインターフェースでテスト済み)IMHO
基本

51

まれに、プライベート関数をテストしたい場合は、通常は保護するように変更し、パブリックラッパー関数を使用してサブクラスを作成しました。

クラス:

...

protected void APrivateFunction()
{
    ...
}

...

テスト用のサブクラス:

...

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

...

1
実際のクラスを散らかす代わりに、その子クラスを単体テストファイルに置くこともできます。狡猾さのために+1。
Tim Abell、2011

可能な場合は常に、ユニットテストプロジェクトにすべてのテスト関連コードを配置します。これは単なる擬似コードでした。
Jason Jackson

2
この関数は非公開ではなく、保護されており、最終的な結果は...コードの安全性を低下させた/子タイプの非公開機能に公開した
War

22

もっと根本的な質問をする必要があると思うのは、そもそもなぜプライベートメソッドをテストしようとしているのかということです。これは、そのクラスのパブリックインターフェイスを介してプライベートメソッドをテストしようとしているコードの匂いですが、そのメソッドは、実装の詳細であるため、理由によりプライベートです。パブリックインターフェイスの動作にのみ注意し、内部での実装方法には注意しないでください。

プライベートメソッドの動作をテストする場合は、一般的なリファクタリングを使用して、そのコードを別のクラスに抽出できます(おそらくパッケージレベルの可視性があるため、パブリックAPIの一部ではないことを確認してください)。その後、その動作を個別にテストできます。

リファクタリングの成果は、プライベートメソッドが、元のクラスのコラボレーターになった別個のクラスになったことを意味します。その動作は、独自の単体テストによって十分に理解されるようになります。

次に、元のクラスをテストするときにその動作を模擬できるので、パブリックインターフェイスとそのすべてのプライベートメソッドの組み合わせの爆発をテストする必要はなく、そのクラスのパブリックインターフェイスの動作をテストすることに集中できます。 。

これは車の運転に似ています。車を運転するときはボンネットを上にして運転しないので、エンジンが作動していることがわかります。エンジンが作動していることを知るために、私は車が提供するインターフェース、つまり回転カウンターとスピードメーターに依存しています。アクセルペダルを踏むと、実際に車が動くことに依存しています。エンジンをテストしたい場合は、それを個別にチェックできます。:D

もちろん、レガシーアプリケーションがある場合、プライベートメソッドを直接テストすることは最後の手段ですが、より良いテストを可能にするためにレガシーコードをリファクタリングすることをお勧めします。マイケルフェザーズはこのテーマについて素晴らしい本を書いています。http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


16
この答えの完全に間違ったロジック。プライベートメソッドが必要な古典的なケースは、パブリックメソッドで再利用するコードが必要な場合です。このコードをPrivMethodに配置します。このメソッドはpublicに公開されるべきではありませんが、PrivMethodに依存するpublicメソッドが本当にそれに依存できることを確認するためのテストが必要です。
ディマ2013

初期開発中には理にかなっていますが、標準の回帰スーツでプライベートメソッドのテストが必要ですか?その場合、実装が変更されると、テストスイートが破損する可能性があります。OTOH、回帰テストが外部から見えるpublicメソッドのみに焦点を当てている場合、privateメソッドが後で壊れた場合でも、回帰スイートはエラーを検出するはずです。その後、必要に応じて、必要に応じて古いプライベートテストを削除できます。
Alex Blakemore 2013

9
同意しない場合は、パブリックインターフェイスのみをテストする必要があります。それ以外の場合は、プライベートメソッドが必要です。その場合は、それらすべてをパブリックにして、すべてをテストします。プライベートメソッドをテストしている場合は、カプセル化を解除しています。プライベートメソッドをテストする必要があり、それが複数のパブリックメソッドで使用されている場合は、それを独自のクラスに移動し、個別にテストする必要があります。すべてのパブリックメソッドは、その新しいクラスに委任する必要があります。元のクラスのインターフェイス。動作が変更されていないことと、委任されたプライベートメソッドに対して個別のテストがあることを確認できます。
ビッグカフナ2013

@Big Kahuna-プライベートメソッドを単体テストする必要があると思われる場合は、大規模で複雑なプロジェクトで作業したことはありません。多くの場合、クライアント固有の検証などのパブリック関数は、コードを読みやすくするために非常に単純なプライベートメソッドを呼び出すだけで20行になりますが、それでも個々のプライベートメソッドをテストする必要があります。public関数を20回テストすると、単体テストが失敗したときにデビューすることが非常に難しくなります。
Pedro.The.Kid 2017年

1
私はFTSE 100の会社で働いています。私の時代にはいくつかの複雑なプロジェクトを見てきました。ありがとうございます。そのレベルまでテストする必要がある場合、個別のコラボレーターとしての各プライベートメソッドは、テストが必要な個別の動作があることを意味するため、個別にテストする必要があります。メインMediatorオブジェクトのテストは、相互作用テストになります。それは正しい戦略が呼び出されていることをテストするだけです。あなたのシナリオは、問題のクラスがSRPに従っていないように聞こえます。変更する理由は1つではありませんが、20 => SRP違反です。GOOS本またはボブおじさんを読んでください。YMWV
ビッグカフナ

17

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

しかし、時々、コンパイル済みまたはサードパーティのアセンブリのハック/探索を行うときに、私自身がプライベートクラスまたはプライベートまたは内部コンストラクターでクラスを初期化したいと思ったことがあります。または、時々、変更できないプリコンパイルされたレガシーライブラリを扱う場合、プライベートメソッドに対していくつかのテストを作成することになります。

こうして生まれた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();

12

2つの方法でプライベートメソッドを単体テストできます

  1. PrivateObjectクラスのインスタンスを作成できます。構文は次のとおりです

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
  2. リフレクションを使用できます。

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });

良い答えですが、#1では構文が間違っています。PrivateClassfirstのインスタンスを宣言して使用する必要があります。stackoverflow.com/questions/9122708/…–
SharpC

10

InternalsVisibleToAttributeメソッドも使用しました。これを実現するために以前のプライベートメソッドを内部化することに不快感を感じる場合は、いずれにしても直接ユニットテストの対象にすべきではないことにも言及する価値があります。

結局のところ、特定の実装ではなく、クラスの動作をテストしています。前者を変更せずに後者を変更しても、テストはパスするはずです。


実装ではなく、動作のテストに関するポイントが気に入っています。単体テストを実装(プライベートメソッド)に関連付けると、テストは脆弱になり、実装が変更されたときに変更する必要があります。
ボブ・ホーン

9

プライベートメソッドには2つのタイプがあります。静的プライベートメソッドと非静的プライベートメソッド(インスタンスメソッド)。次の2つの記事では、プライベートメソッドをユニットテストでユニットテストする方法を説明しています。

  1. 静的プライベートメソッドの単体テスト
  2. 非静的プライベートメソッドの単体テスト

リンクを提供するだけでなく、いくつかの例を提供してください
vinculis

醜く見えます。インテリセンスなし。MSからの不正解。ショックです!
alerya 2014年

プライベート静的メソッドをテストする最も簡単な方法
グラント

8

MS Testには、VSCodeGenAccessorsというファイルを作成することにより、プライベートメンバーとメソッドをプロジェクトで使用できるようにする優れた機能が組み込まれています

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

BaseAccessorから派生したクラスを使用

といった

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }

8
生成されたファイルはVS2005にのみ存在します。2008年にそれらは裏で生成されます。そして、それらは忌まわしいです。また、関連付けられたShadowタスクはビルドサーバーで不安定です。
Ruben Bartelink 2010

また、VS2012-2013ではアクセサーが廃止されました。
Zephan Schroeder

5

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

あなたはここで記事を見つけることができます:

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



4

コンパイラディレクティブはすぐに混乱するため、使用しない傾向があります。それらを本当に必要とする場合にそれを軽減する1つの方法は、それらを部分クラスに配置し、製品版を作成するときにビルドにその.csファイルを無視させることです。


(コンパイラの最適化などをテストするために)製品版にテストアクセサを含めますが、リリースバージョンには含めません。しかし、私は髪を分割していて、とにかくこれに賛成しました。なぜなら、そのようなものを1か所に置くのが良い考えだと思うからです。アイデアをありがとう。
CADは2013年

4

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

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

このため、InternalsVisibleToAtrributeの使用は避けてください。

イアンクーパーによるこのテーマを取り上げた素晴らしい講演は次のとおりです。イアンクーパー:TDD、どこがうまくいかなかったか


3

プライベート宣言をテストするとよい場合があります。基本的に、コンパイラーには1つのパブリックメソッドCompile(string outputFileName、params string [] sourceSFileNames)しかありません。「隠された」各宣言をテストせずにそのようなメソッドをテストするのは難しいと理解していると思います!

これが、テストを簡単にするためにVisual T#を作成した理由です。これは無料の.NETプログラミング言語です(C#v2.0互換)。

「.-」演算子を追加しました。「。」のように動作します。演算子。ただし、テストしたプロジェクトの内容を変更せずに、テストから非表示の宣言にアクセスできます。

当社のウェブサイトを見てみましょう:ダウンロードし、それを無料で


3

まだ誰も言っていないことに驚いていますが、私が採用した解決策は、クラス内に静的メソッドを作成して、それ自体をテストすることです。これにより、テスト用にパブリックおよびプライベートのすべてにアクセスできます。

さらに、(Python、Ruby、PHPなどのオブジェクト指向機能を備えた)スクリプト言語では、実行時にファイル自体をテストできます。変更が何も壊さないことを確認するすばやい方法。これは明らかに、すべてのクラスをテストするためのスケーラブルなソリューションになります。それらをすべて実行するだけです。(他の言語でも、常にテストを実行するvoid mainを使用してこれを行うことができます)。


1
これは実用的ですが、あまりエレガントではありません。これにより、コードベースの混乱が少し発生する可能性があり、実際のコードからテストを分離することもできません。外部でテストする機能は、静的メソッドを手動で作成する代わりに、テストをスクリプトで自動化する機能を開きます。
Darren Reid

これは外部からのテストを妨げるものではありません...静的メソッドを好きなように呼び出すだけです。コードベースも面倒ではありません...それに応じてメソッドに名前を付けます。私は「runTests」を使用していますが、同様の作品は何でも使用できます。
ルーブ

あなたの権利、それは外部でのテストを妨げるものではありませんが、より多くのコードを生成します。つまり、コードベースを乱雑にします。各クラスには、1つ以上のコンストラクターで変数を初期化するかどうかをテストする多くのプライベートメソッドがある場合があります。テストするには、少なくともテストするメソッドと同じ数の静的メソッドを作成する必要があり、テストメソッドは、適切な値を初期化するために大きくする必要がある場合があります。これはコードのメンテナンスをより困難にします。他の人が言ったように、クラスの振る舞いをテストすることはより良いアプローチであり、残りはデバッグするのに十分小さくなければなりません。
Darren Reid

私は他の人と同じ数の行をテストに使用します(実際には、後で読むように少なくなります)。すべてのプライベートメソッドをテストする必要はありません。テストが必要なものだけ:)また、それぞれを個別のメソッドでテストする必要もありません。1回の呼び出しでそれを行います。これは実際に、コードのメンテナンスをLESSにすることを難しくします。すべてのクラスが同じ包括的単体テストメソッドを持っているため、すべてのプライベートおよび保護された単体テストを1行ずつ実行します。次に、テストハーネス全体がすべてのクラスで同じメソッドを呼び出し、メンテナンスはすべて私のクラス内にあります(テストなど)。
ルーブ

3

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

テストケースクラスにこれらのメソッドを含めるだけで、指示されたとおりに使用できます。

  /**
   *
   * @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));

元のプライベート関数に表示される順序でパラメーターを発行するだけです


3

すべての煩わしさなしにプライベートメソッドを実行したい人のために。これは、古き良きReflectionを使用したユニットテストフレームワークで動作します。

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

その後、実際のテストでは、次のようなことができます。

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");

2

MbUnitは、Reflectorと呼ばれるこのラッパーを手に入れました。

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

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

dogReflector.GetProperty("Age");

「テストプライベート」に関しては、私はそれに同意します。プライベートユニットテストを実行しても意味がありません。しかし、現実の世界では、コードをリファクタリングする代わりにプライベートテストを書きたいと思うかもしれません。


4
ただ、情報のために、Reflectorより強力に置き換えられていMirrorガッリオ/ MbUnitのv3.2の中で。(gallio.org/wiki/doku.php?id=mbunit:mirror
ヤンTrevin

2

これは、プライベートメソッドの単体テストに関する優れた記事です。しかし、アプリケーションをテスト用に特別に設計する(テストのみのテストを作成するようなもの)か、テストにリフレクションを使用するのがよいかわかりません。私たちのほとんどが2番目の方法を選択することは間違いありません。


2

私の意見では、クラスのパブリックAPIのみを単体テストする必要があります。

メソッドをユニットテストするためにパブリックにすると、カプセル化が壊れ、実装の詳細が公開されます。

優れたパブリックAPIは、クライアントコードの当面の目標を解決し、その目標を完全に解決します。


これが正解のIMOです。プライベートメソッドがたくさんある場合、これはおそらく、独自のパブリックインターフェイスに分割する必要がある隠しクラスがあるためです。
sunefred

2

私はPrivateObjectクラスを使用しています。ただし、前述のとおり、プライベートメソッドのテストを回避する方が適切です。

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

2
CC -Dprivate=public

「CC」は、私が使用しているシステムのコマンドラインコンパイラです。-Dfoo=barと同等のことを行い#define foo barます。したがって、このコンパイルオプションは、すべてのプライベートなものをパブリックに効果的に変更します。


2
これは何ですか?これはVisual Studioに適用されますか?
YeahStu 2009

「CC」は、私が使用しているシステムのコマンドラインコンパイラです。"-Dfoo = bar"は "#define foo bar"と同等です。したがって、このコンパイルオプションは、すべてのプライベートなものをパブリックに効果的に変更します。ははは!
マークハリソン

Visual Studioで、ビルド環境に定義を設定します。
マークハリソン、

1

次に例を示します。最初はメソッドのシグネチャです。

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);
}

1

これを行う方法は、メソッドprotectedを用意して、テストするクラスを継承するテストフィクスチャを記述することです。このように、メソッドを変更することはありませんがpublic、テストを有効にします。


コンシューマが基本クラスから継承し、保護された関数を使用することも許可するため、これには同意しません。これは、これらの関数をプライベートまたは内部にすることによって、最初に防止したいと思っていました。
NickN。

1

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

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

  • リフレクションを使用する(複雑に)
  • 同じクラスで単体テストを記述します(テストコードも含まれているため、製品コードが見苦しくなります)。
  • 何らかの種類のユーティリティクラスでメソッドをリファクタリングしてパブリックにする
  • @VisibleForTestingアノテーションを使用してプライベートを削除する

私は、最も単純で最も複雑でないアノテーション方式を好みます。唯一の問題は、私があまり気にしないと思う視認性を高めたことです。常にインターフェイスにコーディングする必要があるため、インターフェイスMyServiceと実装MyServiceImplがある場合、対応するテストクラスであるMyServiceTest(テストインターフェイスメソッド)とMyServiceImplTest(テストプライベートメソッド)を使用できます。とにかく、すべてのクライアントはインターフェイスを使用する必要があります。そのため、プライベートメソッドの可視性が向上しても、実際には問題になりません。


1

debug-Modeでビルドするときに、それをパブリックまたは内部として(InternalsVisibleToAttributeを使用して)宣言することもできます。

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

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


0

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

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


0

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


2
いや、InternalsVisibleToAttributeないない 必要あなたのアセンブリが強く命名されていること。現在、そうではないプロジェクトで使用しています。
コーディグレイ

1
これを明確にするには、「現在のアセンブリとフレンドアセンブリの両方が署名されていないか、両方とも厳密な名前で署名されている必要があります。」-MSDNから
Hari
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.