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


104

NUnitを正しく使用する方法を知りたいです。最初に、メインプロジェクトを参照として使用する別のテストプロジェクトを作成しました。しかしその場合、私はプライベートメソッドをテストすることができません。私の推測では、メインコードにテストコードを含める必要がありますか?-それは正しい方法ではないようです。(コードにテストを組み込んで出荷するという考えは嫌いです。)

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

回答:


74

一般に、ユニットテストは、クライアントの観点から結果が正しい限り、実装は重要ではないという理論に基づいて、クラスのパブリックインターフェイスに対処します。

そのため、NUnitは非公開メンバーをテストするためのメカニズムを提供していません。


1
+1私はこの問題に直面しました。私の場合、私がパブリックをユニットテストした場合、プライベートとパブリックの意味で発生する「マッピング」アルゴリズムがあり、それは実際には統合テストになります。このシナリオでは、テストポイントをコードの設計上の問題に書き込もうとしていると思います。その場合、おそらくその「プライベート」メソッドを実行する別のクラスを作成する必要があります。
アンディ10/07/19

140
私はこの議論に完全に同意しません。単体テストはクラスに関するものではなく、コードに関するものです。パブリックメソッドが1つあるクラスがあり、パブリックメソッドの結果を作成するために10個のプライベートメソッドが使用されている場合、各プライベートメソッドが意図したとおりに機能することを確認する方法がありません。適切な方法で適切なプライベートメソッドにヒットするパブリックメソッドでテストケースを作成してみることができます。しかし、パブリックメソッドが決して変更されないと信頼しない限り、プライベートメソッドが実際に呼び出されたかどうかはわかりません。プライベートメソッドは、作成者ではなく、コードのプロダクションユーザー専用にすることを目的としています。
Quango

3
@Quango、標準のテスト設定では、「作成者」コードのプロダクションユーザーです。それでも、設計上の問題のにおいがしない場合は、クラスを変更せずに非パブリックメソッドをテストするオプションがあります。 System.Reflectionバインディングフラグを使用して非パブリックメソッドにアクセスして呼び出すことができるため、NUnitをハックしたり、独自のフレームワークを設定したりできます。または(もっと簡単だと思います)、コンパイル時フラグ(#if TESTING)を設定してアクセス修飾子を変更し、既存のフレームワークを使用できるようにすることもできます。
harpo

23
プライベートメソッドは、実装の詳細です。単体テストを持つことの多くのポイントは、動作を変更せずに実装のリファクタリングを可能にすることです。プライベートメソッドが非常に複雑になり、個別にテストでカバーしたい場合は、この補題を使用できます。「プライベートメソッドは、常に別のクラスのパブリック(または内部)メソッドにすることができます」。つまり、ロジックの一部がテストの対象となるほど十分に進んでいる場合、おそらく独自のテストを備えた独自のクラスになる可能性があります。
Anders Forsgren 2013年

6
@AndersForsgrenしかし、ユニットテストのポイントは、統合テストや機能テストとは対照的に、そのような詳細をテストすることです。また、コードカバレッジは重要な単体テスト指標と見なされているため、理論的には、すべてのロジックが「テストの対象となるのに十分なほど高度」です。
カイルストランド

57

単体テストの焦点はパブリックインターフェイスであることには同意しますが、プライベートメソッドもテストすると、コードの印象がはるかに細かくなります。MSテストフレームワークでは、PrivateObjectおよびPrivateTypeを使用してこれを可能にしますが、NUnitではできません。私が代わりに行うことは:

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

この方法は、テスト性を優先してカプセル化を妥協する必要がないことを意味します。プライベート静的メソッドをテストする場合は、BindingFlagsを変更する必要があることに注意してください。上記の例は、単なるインスタンスメソッドです。


7
これらの小さなヘルパーメソッドをテストすると、ユニットテストでユニットパーツがキャプチャされます。すべてがユーティリティクラスに適しているわけでもありません。
Johan Larsson

12
これはまさに私が必要としたものです。ありがとう!私が必要とするのは、プライベートフィールドが適切に設定されていることを確認することだけであり、パブリックインターフェイスを介してそれを判別する方法を理解するために3時間を費やす必要はありませんでした。これは気まぐれでリファクタリングできない既存のコードです。単純な質問が理想主義的な教義でどのように答えられるかはおかしいです
Droj

5
これが実際に質問に回答する唯一の回答であるため、これが選択された回答である必要があります。
bavaza 2013

6
同意した。選ばれた答えにはメリットがありますが、OPの質問ではなく、「プライベートメソッドをテストすべきか」に対する答えです。
chesterbr 2014年

2
それは動作します。ありがとう!これは、for.Thisを検索している多くの人が選択した回答がどうあるべきかである
エイブル・ジョンソン

47

単体テストを記述する一般的なパターンは、パブリックメソッドのみをテストすることです。

テストしたいプライベートメソッドがたくさんある場合は、通常、これはコードをリファクタリングする必要のある兆候です。

これらのメソッドを、現在住んでいるクラスで公開するのは誤りです。それはあなたがそのクラスに持たせたい契約を破るでしょう。

それらをヘルパークラスに移動し、そこで公開するのが正しい場合があります。このクラスは、APIによって公開されない場合があります。

このようにして、テストコードがパブリックコードと混合されることはありません。

同様の問題は、プライベートクラスのテストです。アセンブリからエクスポートしないクラス。この場合、InternalsVisibleTo属性を使用して、テストコードアセンブリを製品コードアセンブリのフレンドにすることができます。


1
+ 1はい!私はテストを書いているときにその結論に達しました。良いアドバイスです!
アンディ

1
テストしたいがAPIのユーザーには公開しないプライベートメソッドを、公開されていない別のクラスに移動して公開すると、まさに私が探していたものになります。
トーマスN

@morechilliに感謝します。これまでにInternalsVisibleToを見たことがない。テストがはるかに簡単になりました。
Andrew MacNaughton 2014年

こんにちは。最近コメントなしでいくつかの反対票を受け取りました。あなたの考えや懸念を説明するためにいくつかのフィードバックを提供してください。
morechilli

6
したがって、クラス内の複数の場所で呼び出されて、そのクラスだけが必要とする非常に具体的な何かを行うプライベートメソッドがあり、パブリックAPIの一部として公開されない場合は、ヘルパーに入れます。テストするだけのクラス?クラスに公開するだけでもよいでしょう。
Daniel Macias、2015年

21

テストアセンブリを、テストするターゲットアセンブリのフレンドアセンブリとして宣言することにより、プライベートメソッドをテストすることができます。詳細については、以下のリンクを参照してください。

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

これは、テストコードと本番コードをほとんど分離するので便利です。私はこの方法の必要性を見つけたことがないので、自分でこの方法を使用したことはありません。コードを使用してコードを処理する方法を確認するために、テスト環境で単純に複製できない極端なテストケースを試し、テストするためにそれを使用できると思います。

ただし、既に述べたように、プライベートメソッドをテストする必要はありません。リクリー以上に、コードを小さな構成要素にリファクタリングしたいと考えています。リファクタリングを行うときに役立つヒントの1つは、システムが関連するドメインについて考え、このドメインに存在する「実際の」オブジェクトについて考えることです。システム内のオブジェクト/クラスは、実際のオブジェクトに直接関連付ける必要があります。これにより、オブジェクトに含まれるべき正確な動作を分離し、オブジェクトの責任を制限することができます。これは、特定のメソッドをテストできるようにするだけでなく、論理的にリファクタリングしていることを意味します。オブジェクトの動作をテストできます。

それでも内部テストの必要性を感じている場合は、1つのコードに集中したいので、テストのモックを検討することもできます。モックとは、オブジェクトの依存関係をそこに注入するところですが、注入されたオブジェクトは「実際の」オブジェクトでもプロダクションオブジェクトでもありません。これらは、動作エラーを簡単に分離できるようにハードコードされた動作を持つダミーオブジェクトです。Rhino.Mocksは、基本的にオブジェクトを作成する人気のある無料のモックフレームワークです。TypeMock.NET(コミュニティエディションが利用可能な市販製品)は、CLRオブジェクトをモックできるより強力なフレームワークです。たとえば、データベースアプリのテスト時にSqlConnection / SqlCommandクラスとDatatableクラスをモックする場合に非常に役立ちます。

うまくいけば、この回答が一般的な単体テストについての情報を提供し、単体テストからより良い結果を得るのに役立つことを願っています。


12
プライベートではなく、内部メソッドをテストできます(NUnitを使用)。
TrueWill、

6

私は、プライベートメソッドをテストする機能があることに賛成です。xUnitが開始されたとき、それはコードが書かれた後に機能をテストするためのものでした。この目的には、インターフェースのテストで十分です。

ユニットテストはテスト駆動開発に進化しました。すべてのメソッドをテストする機能があると、そのアプリケーションで役立ちます。


5

この質問はその発展年にありますが、私はこれを行う方法を共有したいと思いました。

基本的に、私はすべての単体テストクラスを、そのアセンブリの「デフォルト」の下にある「UnitTest」名前空間でテストしているアセンブリに入れます。各テストファイルは次のようにラップされます。

#if DEBUG

...test code...

#endif

ブロックとは、a)リリースで配布されていないこと、b)フープジャンプなしでinternal/ Friendレベルの宣言を使用できることを意味します。

これが提供するもう1つのことは、この質問にさらに関連して、partialプライベートメソッドをテストするためのプロキシを作成するために使用できるクラスの使用です。たとえば、整数値を返すプライベートメソッドのようなものをテストします。

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

アセンブリのメインクラスとテストクラス:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

明らかに、開発中にこのメソッドを使用しないことを確認する必要がありますが、Releaseビルドは、そうした場合、すぐに不注意な呼び出しを示します。


4

ユニットテストの主な目的は、クラスのパブリックメソッドをテストすることです。これらのパブリックメソッドは、これらのプライベートメソッドを使用します。単体テストでは、公開されているものの動作をテストします。


3

これで問題が解決しない場合はお詫びしますが、リフレクションの使用、#if #endifステートメント、プライベートメソッドの表示などの解決策では問題は解決しません。プライベートメソッドが表示されない理由はいくつか考えられます。たとえば、本番用コードで、チームがユニットテストを遡及的に作成している場合はどうでしょうか。

私がMSTestのみで作業しているプロジェクトの場合(悲しいことに)、アクセサを使用して、プライベートメソッドを単体テストする方法があるようです。


2

プライベート関数はテストしません。リフレクションを使用してプライベートメソッドとプロパティに入る方法があります。しかし、それは本当に簡単ではなく、私はこの習慣を強くお勧めしません。

公開されていないものはテストしないでください。

内部メソッドとプロパティがある場合は、それをパブリックに変更するか、テストをアプリに同梱することを検討する必要があります(私が実際に問題として認識していないもの)。

顧客がテストスイートを実行して、提供したコードが実際に「機能している」ことを確認できれば、これは問題ではありません(これによってIPを提供しない限り)。すべてのリリースに含めるものは、テストレポートとコードカバレッジレポートです。


一部の顧客は、予算を小さく保ち、テストの範囲についての議論を開始しようとします。テストを書くのはあなたが悪いコーダーであり、自分のスキルを信頼していないからではないことをこれらの人々に説明するのは難しいです。
MrFox 2008年

1

ユニットテストの理論では、契約のみをテストする必要があります。つまり、クラスのパブリックメンバーのみです。しかし実際には、開発者は通常、内部メンバーをテストしたいと考えています。-そして、それは悪くありません。はい、それは理論に反しますが、実際にはそれは時々役立つことがあります。

したがって、内部メンバーを実際にテストする場合は、次のいずれかの方法を使用できます。

  1. メンバーを公開します。多くの本で、著者はこのアプローチを単純であると提案しています
  2. メンバーを内部にして、InternalVisibleToをアセンブリに追加できます
  3. クラスメンバーを保護し、テスト中のクラスからテストクラスを継承できます。

コード例(疑似コード):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}

ゴキブリはサンプルコード内に隠されているようです;)NUnitのフィクスチャ内のメソッドはパブリックでなければなりませんMessage: Method is not public
Gucu112 2017年

@ Gucu112ありがとう。それを私が直した。目標は設計視点からのアプローチを示すことなので、一般的には問題ではありません。
burzhuy 2017年

1

メソッドを内部で保護してassembly: InternalsVisibleTo("NAMESPACE") から、テスト名前空間に使用 できます。

したがって、NO!プライベートメソッドにアクセスすることはできませんが、これは回避策です。


0

プライベートメソッドパッケージを表示します。そうすれば、これらのメソッドをテストしながら、それを適度にプライベートに保つことができます。テストする必要があるのはパブリックインターフェイスだけであるという人々の意見には同意しません。多くの場合、プライベートメソッドには非常に重要なコードがあり、外部インターフェイスを経由するだけでは適切にテストできません。

ですから、正しいコードや情報の隠蔽についてもっと気にかけるかどうかということは、実際には結局のところです。これらのメソッドにアクセスするには、誰かが自分のクラスをパッケージに配置する必要があるため、パッケージの可視性は良い妥協だと思います。それは本当にそれが本当に賢いことかどうかを二度考えさせるでしょう。

ちなみに私はJavaの男なので、visibliltyパッケージはC#ではまったく別のものと呼ばれるかもしれません。これらのメソッドにアクセスするために、2つのクラスが同じ名前空間にある必要がある場合です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.