C#.NETで静的である依存関係を単体テスト(モック/スタブ)する方法は3つしかないことがわかりました。
これらのうち2つは無料ではなく、1つはリリース1.0に達していないため、静的なもののモックはそれほど簡単ではありません。
それは静的メソッドやそのような「悪」(単体テストの意味で)ですか?もしそうなら、なぜresharperは私に静的なもの、静的なものを作ることを望んでいるのですか? (リシャーパーが「悪」でもないと仮定します。)
説明:メソッドの単体テストを行い、そのメソッドがで静的メソッドを呼び出すシナリオについて話している異なるユニット/クラス。ユニットテストのほとんどの定義では、テスト中のメソッドに他のユニット/クラスの静的メソッドを呼び出させるだけの場合、ユニットテストはでない、あなたはintegrationテストです。 (便利ですが、単体テストではありません。)
ここで他の答えを見ると、静的な状態を保持したり副作用を引き起こしたりする静的メソッド(本当に悪い考えのように聞こえます)と、値を返すだけの静的メソッドの間には混乱があるかもしれません。
状態を保持せず、副作用を引き起こさない静的メソッドは、簡単に単体テストできます。実際、私はそのような方法を「貧乏人」形式の関数型プログラミングと見なしています。メソッドにオブジェクトまたは値を渡すと、オブジェクトまたは値が返されます。これ以上何もない。そのような方法が単体テストに悪影響を及ぼすことはまったくわかりません。
Static dataとstatic methodsを混同しているようです。私が正しく覚えている場合、Resharperは、クラス内でprivate
メソッドを静的にできる場合はそれを静的にすることをお勧めします。これにより、パフォーマンスが少し向上すると思います。 しないでください「できるすべてのもの」を静的にすることをお勧めします!
静的メソッドに問題はなく、テストは簡単です(静的データを変更しない限り)。たとえば、数学ライブラリを考えてみてください。これは、静的メソッドを持つ静的クラスに適した候補です。あなたがこのような(工夫された)メソッドを持っているなら:
public static long Square(int x)
{
return x * x;
}
これは非常にテスト可能であり、副作用はありません。たとえば、20を渡すと400が返されることを確認するだけです。問題ありません。
ここでの実際の質問が「このコードをテストするにはどうすればよいですか?」
public class MyClass
{
public void MethodToTest()
{
//... do something
MyStaticClass.StaticMethod();
//...more
}
}
次に、コードをリファクタリングして、通常のように静的クラスの呼び出しを次のように挿入します。
public class MyClass
{
private readonly IExecutor _externalExecutor;
public MyClass(_IExecutor executor)
{
_exeternalExecutor = executor;
}
public void MethodToTest()
{
//... do something
_exetrnalExecutor.DoWork();
//...more
}
}
public class MyStaticClassExecutor : IExecutor
{
public void DoWork()
{
MyStaticClass.StaticMethod();
}
}
Staticsは必ずしも悪であるとは限りませんが、Fakes/Mocks/Stubsを使用した単体テストに関しては、選択肢が制限される可能性があります。
モックには2つの一般的なアプローチがあります。
最初の1つ(従来-RhinoMocks、Moq、NMock2によって実装され、手動のモックとスタブもこのキャンプにあります)は、テストシームと依存性注入に依存しています。いくつかの静的コードを単体テストしていて、依存関係があるとします。このように設計されたコードで頻繁に発生するのは、staticが独自の依存関係を作成し、dependency inversionを反転させることです。この方法で設計されたテスト対象のコードにモックインターフェイスを挿入できないことがすぐにわかります。
2つ目(TypeMock、JustMock、およびMolesによって実装されたすべてのものをモックする)は、.NETの Profiling API に依存しています。それはあなたのCIL命令のいずれかを傍受し、あなたのコードのチャンクを偽物で置き換えることができます。これにより、このキャンプのTypeMockおよびその他の製品は、静的、シールされたクラス、プライベートメソッドなど、テスト可能に設計されていないものをモックできます。
2つの学派の間で議論が続いています。 1つは、SOLIDの原則に従い、テストしやすいように設計することです(多くの場合、静力学を容易にすることを含みます)。もう1つはTypeMockを購入すれば心配しないでください。
これをチェックしてください: "Static Methods are Death to Testability" 。議論の短い要約:
単体テストを行うには、コードの小さな部分を取り、その依存関係を再配線し、分離してテストする必要があります。これは、静的メソッドではグローバル状態にアクセスする場合だけでなく、他の静的メソッドを呼び出すだけの場合でも困難です。
単純な真実がめったに認められないのは、クラスに別のクラスへのコンパイラから見える依存関係が含まれている場合、そのクラスから分離してcannotがテストされるということです。テストのように見せかけることができ、レポートにテストのように表示されます。
ただし、テストの主要な定義プロパティはありません。物事が間違っているときに失敗し、正しいときに合格する。
これは、静的呼び出し、コンストラクター呼び出し、および基本クラスまたはインターフェイスから継承されていないメソッドまたはフィールドへの参照に適用されます。クラス名がコードに表示される場合、それはコンパイラから見える依存関係であり、それなしでは有効にテストできません。小さなチャンクは単に有効なテスト可能な単位ではありませんです。あたかもそれをあたかも扱ったとしても、テストフレームワークが「テスト合格」と言うために使用するXMLを出力する小さなユーティリティを書くよりも意味のない結果になります。
その場合、3つのオプションがあります。
ユニットテストを定義して、クラスとそのハードコードされた依存関係で構成されるユニットのテストを行います。これは機能し、循環依存関係を回避します。
テストを担当するクラス間にコンパイル時の依存関係を作成しないでください。結果として得られるコードスタイルを気にしない限り、これは機能します。
単体テストではなく、統合テストを実行してください。これは機能しますが、統合テストという用語を使用する必要がある他のものと競合しない場合に限ります。
それについて二つの方法はありません。 ReSharperの提案とC#のいくつかの便利な機能は、すべてのコードに対して分離されたアトミックユニットテストを作成している場合は、それほど頻繁には使用されません。
たとえば、静的メソッドがあり、それをスタブ化する必要がある場合は、プロファイルベースの分離フレームワークを使用しない限りできません。呼び出し互換の回避策は、メソッドの先頭を変更してラムダ表記を使用することです。例えば:
前:
public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
}
後:
public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
};
2つは呼び出し互換です。呼び出し元を変更する必要はありません。関数の本体は同じままです。
次に、ユニットテストコードで、この呼び出しを次のようにスタブできます(Databaseと呼ばれるクラスにあると想定)。
Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }
完了したら、元の値に置き換えてください。これは、try/finallyを使用して行うか、ユニットテストのクリーンアップで、すべてのテストの後に呼び出されるものを使用して、次のようなコードを記述できます。
[TestCleanup]
public void Cleanup()
{
typeof(Database).TypeInitializer.Invoke(null, null);
}
これにより、クラスの静的初期化子が再度呼び出されます。
Lambda Funcsは通常の静的メソッドほどサポートが豊富ではないため、このアプローチには次の望ましくない副作用があります。
しかし、静的を完全に避け、これをインスタンスメソッドに変換するとします。メソッドが仮想であるか、インターフェースの一部として実装されていない限り、それはまだモック可能ではありません。
したがって、実際には、静的メソッドをスタブ化するための救済策を提案する人は、それらをインスタンスメソッドにすることです。また、仮想メソッドやインターフェイスの一部ではないインスタンスメソッドにも反対します。
では、なぜC#に静的メソッドがあるのでしょうか。非仮想インスタンスメソッドを使用できるのはなぜですか?
これらの「機能」のいずれかを使用する場合、分離されたメソッドを作成することはできません。
では、いつそれらを使用しますか?
誰もがスタブしたいと思わないコードにそれらを使用してください。いくつかの例:StringクラスのFormat()メソッド、ConsoleクラスのWriteLine()メソッド、MathクラスのCosh()メソッド
そしてもう1つ..ほとんどの人はこれを気にしませんが、間接呼び出しのパフォーマンスについて可能であれば、インスタンスメソッドを回避するもう1つの理由です。パフォーマンスが低下する場合があります。そもそも非仮想メソッドが存在するのはそのためです。
長い間、誰もまだ本当に簡単な事実を述べていないことがわかります。メソッドを静的にすることができるとresharperが言った場合、それは私にとって大きなことを意味します、彼の声が私に言うのを聞くことができます。ヘルパークラスか何かで」。
この提案を行うツールはR#だけではありません。 FxCop/MSコード分析も同じようにします。
私は一般的に、メソッドが静的である場合、一般に、そのままテスト可能であるべきだと言っています。これは、いくつかの設計上の考慮事項をもたらし、おそらく私が今考えているよりも多くの議論をもたらすので、辛抱強く反対票とコメントを待っています...;)
静的メソッドが他のメソッド内から呼び出された場合、そのような呼び出しを防止または置換することはできません。つまり、これらの2つのメソッドは単一のユニットを作成します。あらゆる種類の単体テストは、両方をテストします。
この静的メソッドがインターネットと通信したり、データベースを接続したり、GUIポップアップを表示したり、ユニットテストを完全な混乱に変換したりする場合、簡単な回避策はありません。このような静的メソッドを呼び出すメソッドは、単体テストの恩恵を受ける純粋な計算コードが多数ある場合でも、リファクタリングなしではテストできません。
私は、Resharperがガイダンスを提供し、セットアップ時に使用したコーディングガイドラインを適用していると思います。 Resharperを使用していて、メソッドは静的である必要があると言われている場合、インスタンス変数には作用しないプライベートメソッドにバインドされています。
テスト可能性については、とにかくプライベートメソッドをテストしてはならないので、このシナリオは問題になりません。
公開されている静的メソッドのテスト容易性については、静的メソッドが静的状態に触れると、単体テストが難しくなります。個人的には、これを最小限に抑え、テストフィクスチャを介して制御できる依存関係がメソッドに渡される場合は、静的メソッドをできるだけ純粋な関数として使用します。ただし、これは設計上の決定です。