15

私は現在 C# での開発に携わっています - ここにいくつかの背景があります: クライアント アプリケーションで MVP を実装し、5 を超える循環的複雑度を持つメソッドは存在しないという循環的規則があります。これにより、多くの小さなプライベート メソッドが発生します。これらは通常、1 つのことを担当します。

私の質問は、クラスの単体テストに関するものです。

パブリック メソッドを使用したプライベート実装のテストはすべて問題ありません...これを実装するのに問題はありません。

しかし...次の場合はどうでしょうか。

例 1.非同期データ取得リクエストの結果を処理します (コールバック メソッドは、純粋にテストのために公開するべきではありません)。

例 2.操作を行うイベント ハンドラー (ビュー ラベルのテキストの更新など - 私が知っているばかげた例...)

例 3.保護された仮想メソッドをオーバーライドして拡張できるサードパーティ フレームワークを使用しています (パブリック メソッドからこれらの仮想メソッドへのパスは、一般にブラック ボックス プログラミングとして扱われ、フレームワークが提供するあらゆる種類の依存関係があります。あなたは知りたくない)

上記の例は、設計が不十分な結果であるとは思えません。また、そのようなメソッドはコンテキストを失うため、分離してテストするために別のクラスに移動する候補にはなりません。

これについて考えている人はいますか?

乾杯、ジェイソン

編集: 元の質問では十分に明確ではなかったと思います-アクセサーを使用してプライベートメソッドをテストし、TypeMock を使用して呼び出し/メソッドをモックできます。それは問題ではありません。問題は、公開する必要がない、または公開できないものをテストすることです。

セキュリティの抜け穴を導入する可能性があるため、テストのためにコードを公開したくありません (これを非表示にするためのインターフェイスを公開することはオプションではありません。オブジェクトを元の型にキャストして戻すことができるため、私はしたくありません)

テストのために別のクラスにリファクタリングされるコードは問題ありませんが、コンテキストが失われる可能性があります。私はいつも、特定のコンテキストのないコードのポットを含むことができる「ヘルパー」クラスを持つのは悪い習慣だと思っていました - (ここでSRPを考えてください)。これがイベントハンドラーでも機能するとは本当に思いません。

私は間違っていることが証明されてうれしいです - この機能をテストする方法がわかりません! 私は常に、それが壊れたり変更されたりする可能性がある場合は、テストすることを心に留めていました.

乾杯、ジェイソン

4

12 に答える 12

7

メソッドの循環的複雑度が 5 を超えてはならないという循環的規則があります。

私はそのルールが好きです。

ポイントは、プライベート メソッドが実装の詳細であるということです。それらは変更/リファクタリングされる可能性があります。パブリック インターフェイスをテストします。

複雑なロジックを持つプライベート メソッドがある場合は、それらを別のクラスにリファクタリングすることを検討してください。これは、循環的な複雑さを抑えるのにも役立ちます。別のオプションは、メソッドを内部にし、InternalsVisibleTo を使用することです (Chris の回答のリンクの 1 つに記載されています)。

キャッチは、プライベート メソッドで参照される外部依存関係がある場合に発生する傾向があります。ほとんどの場合、依存性注入などの手法を使用して、クラスを分離できます。サードパーティのフレームワークを使用した例では、それは難しいかもしれません。最初に設計をリファクタリングして、サードパーティの依存関係を分離しようとします。それが不可能な場合は、Typemock Isolatorの使用を検討してください。私はそれを使用していませんが、その重要な機能は、プライベート、静的などのメソッドを「モック」できることです。

クラスはブラックボックスです。そのようにテストします。

編集:私の回答に対するジェイソンのコメントと元の質問の編集に応答しようとします。第一に、 SRPはクラスから離れるのではなく、より多くのクラスを推進していると思います。はい、Swiss Army ヘルパー クラスは避けるのが最善です。しかし、非同期操作を処理するように設計されたクラスはどうでしょうか? それともデータ取得クラスですか?これらは元のクラスの責任の一部ですか、それとも分離できますか?

たとえば、このロジックを別のクラス (内部である可能性があります) に移動するとします。そのクラスは、メソッドが同期的に呼び出されるか非同期的に呼び出されるかを呼び出し元が選択できるようにする非同期デザイン パターンを実装します。単体テストまたは統合テストは、同期メソッドに対して作成されます。非同期呼び出しは標準パターンを使用し、複雑さは低くなります。私たちはそれらをテストしません (受け入れテストを除く)。非同期クラスが内部の場合は、InternalsVisibleTo を使用してテストします。

于 2009-10-02T01:54:55.413 に答える
4

実際に考慮する必要があるのは、次の 2 つのケースだけです。

  1. プライベート コードがパブリック コードから直接的または間接的に呼び出され、
  2. プライベート コードはパブリック コードから呼び出されません。

最初のケースでは、プライベート コードは、プライベート コードを呼び出すパブリック コードを実行するテストによって自動的にテストされるため、プライベート コードをテストする必要はありません。2 番目のケースでは、プライベート コードをまったく呼び出すことができないため、テストせずに削除する必要があります。

したがって、プライベート コードを明示的にテストする必要はありません。

TDD を実行する場合、テストされていないプライベート コードが存在することさえあり得ないことに注意してください。TDD を実行する場合、プライベート コードを表示できる唯一の方法は、公開コードから {メソッド|クラス|...} リファクタリングを抽出することです。また、リファクタリングは、定義上、動作を維持するため、テスト カバレッジを維持します。そして、公開コードが現れる唯一の方法は、テストの失敗の結果です。パブリック コードは、テストが失敗した結果として、既にテスト済みのコードとしてのみ表示され、プライベート コードは、動作を保持するリファクタリングによってパブリック コードから抽出された結果としてのみ表示される場合、テストされていないプライベート コードは決して表示されないことになります。

于 2009-10-02T14:24:06.130 に答える
2

ここには素晴らしい答えがいくつかあります。基本的に、新しいクラスを発芽させるという繰り返しのアドバイスに同意します。ただし、例 3 には、卑劣で単純な手法があります。

例 3. 保護された仮想メソッドをオーバーライドして拡張できるサードパーティ フレームワークを使用しています (パブリック メソッドからこれらの仮想メソッドへのパスは、一般にブラック ボックス プログラミングとして扱われ、フレームワークが提供するあらゆる種類の依存関係があります。あなたは知りたくない)

MyClass が FrameworkClass を拡張するとしましょう。MyTestableClass で MyClass を拡張し、必要な MyClass の保護されたメソッドを公開するパブリック メソッドを MyTestableClass に提供します。優れたプラクティスではありません - 悪い設計を助長するようなものですが、時には役に立ち、非常にシンプルです。

于 2009-10-03T14:28:22.640 に答える
2

C# を使っている TDD 担当者からのいくつかのポイント:

1) インターフェイスにプログラムする場合、インターフェイスにないクラスのメソッドは事実上プライベートです。これは、テスト容易性を促進するためのより良い方法であり、インターフェースを使用するためのより良い方法でもあると思うかもしれません。これらをパブリック メンバーとしてテストします。

2) これらの小さなヘルパー メソッドは、より適切には他のクラスに属している可能性があります。機能の羨望を探してください。元のクラス (あなたがそれを見つけた) のプライベート メンバーとして合理的でないものは、それがうらやむクラスの合理的なパブリック メソッドである可能性があります。これらをパブリック メンバーとして新しいクラスでテストします。

3) いくつかの小さなプライベート メソッドを調べると、それらにまとまりがあることがわかる場合があります。それらは、元のクラスとは別の、関心のある小さなクラスを表す場合があります。その場合、そのクラスはすべてのパブリック メソッドを持つことができますが、元のクラスのプライベート メンバーとして保持されるか、関数内で作成および破棄される可能性があります。これらをパブリック メンバーとして新しいクラスでテストします。

4)元のクラスから「テスト可能な」クラスを派生させることができます。このクラスでは、古いプライベートメソッドを呼び出すだけの新しいパブリックメソッドを作成するのは簡単なタスクです。テスト可能なクラスはテスト フレームワークの一部であり、製品コードの一部ではないため、特別なアクセス権を持つことはクールです。公開されているかのように、テスト フレームワークでテストします。

これらすべてにより、現在プライベート ヘルパー メソッドであるメソッドをテストすることは、IntelliSense の動作を台無しにすることなく簡単に行うことができます。

于 2009-10-02T21:54:25.757 に答える
1

アクセサーファイルは機能しますか? http://msdn.microsoft.com/en-us/library/bb514191.aspx 私はそれらを直接使用したことはありませんが、同僚がいくつかの Windows フォームでプライベート メソッドをテストするためにそれらを使用したことは知っています。

于 2009-10-02T21:59:15.590 に答える
1

プライベート メソッドを直接テストするべきではない、または別のクラスに移動する必要があるという回答が何人かの人々から寄せられました。これは良いことだと思いますが、それだけの価値がない場合もあります。私は原則としてこれに同意しますが、これは負の影響なしに時間を節約するために破ることができるルールの 1 つであることを発見しました。関数が小さい/単純な場合、別のクラスとテスト クラスを作成するオーバーヘッドは過剰です。これらのプライベート メソッドを公開しますが、インターフェイスには追加しません。このようにして、クラスの消費者 (IoC ライブラリを介してのみインターフェイスを取得する必要があります) が誤ってそれらを使用することはありませんが、テストに使用できます。

コールバックの場合、これはプライベート プロパティをパブリックにすることで、テストの記述と保守がはるかに簡単になる好例です。たとえば、クラス A がクラス B にコールバックを渡す場合、そのコールバックをクラス A のパブリック プロパティにします。クラス A の 1 つのテストでは、渡されたコールバックを記録する B のスタブ実装を使用します。コールバックは、適切な条件下で B に渡されます。クラス A の別のテストでは、コールバックを直接呼び出して、適切な副作用があることを確認できます。

このアプローチは、非同期の動作を検証するのに最適だと思います。私は、いくつかの JavaScript テストといくつかの Lua テストでそれを行ってきました。利点は、2 つの小さな単純なテスト (コールバックが設定されていることを確認するテストと、期待どおりに動作することを確認するテスト) があることです。コールバックを非公開にしようとすると、コールバックの動作を検証するテストでさらに多くの設定を行う必要があり、その設定は他のテストで行うべき動作と重複します。カップリングが悪い。

きれいではありませんが、うまく機能すると思います。

于 2009-10-02T22:06:50.210 に答える
0

C# では、AssemblyInfo.cs で属性を使用できます。

[assembly: InternalsVisibleTo("Worker.Tests")]

プライベート メソッドを内部でマークするだけで、テスト プロジェクトは引き続きそのメソッドを参照できます。単純!すべての TDD ナンセンスなしで、カプセル化を維持し、テストを行うことができます。

于 2016-12-29T05:22:50.093 に答える
0

プライベート コードをテストしないでください。後でリファクタリングするときに後悔することになります。次に、Joel のように、コードを使用してテストを常にリファクタリングする必要があるため、TDD がどのように機能しすぎるかについてブログを書いてください。

適切なブラック ボックス テストを行うテクニック (モック、スタブ) があります。彼らを見上げて。

于 2009-10-02T22:05:48.730 に答える