web-dev-qa-db-ja.com

テストを容易にすることを唯一の目的とする製品コードの名前/修正?

リンクされた「重複する」質問は、求められているため、せいぜい不鮮明な一致です

  • is _pattern X_ OK([〜#〜] yes [〜#〜]/[〜#〜] no [〜#〜]

そして、私は明らかに[〜#〜] no [〜#〜]キャンプにいるので、その後尋ねます

  • _pattern X_と呼ばれるもの
  • _pattern X_を修正するために実行できる手順

(どちらもリンクされた質問では扱われません)。


最近、次のようなコードブロックについてコードレビューを行いました。

_public class MyClass
{
    private ISomething mySomething;
    // ...Other variables omitted for brevity

    public MyClass() { mySomething = new Something();  }

    /// <summary>
    /// Constructor - ONLY USE THIS FOR UNIT TESTING
    /// </summary>
    public MyClass(ISomething something) { mySomething = something; }

    public void MyMethod()
    {
        // Gets called by the framework, and changes the internal state of the class by using mySomething...
    }

    // Other methods...
}
_

特に、オーバーロードされたコンストラクタに関心があります。これは、このクラスをテストするためだけに追加されたもので、本番コードに組み込まれます。

このパターン/アンチパターンの名前はありますか?それを解決するために何ができますか?


明確にするために、Somethingの実装は、特にMyClassにオーバーロードされたコンストラクターを追加できるようにするために追加されました。それはnowhere以外で使用されます。その存在は私が心配しているまさにその問題の例です。

ISomethingMyClassと密接に結びついています。抽出する必要はありません。実装とインターフェースも次のようになります。

_public interface ISomething
{
    string GetClassName();
}

public class Something : ISomething
{
    public string GetClassName() { return "MyClass"; }
}
_

つまり、MyClass.MyMethod()の本体を_return "MyClass";_に置き換えるだけです。

ただし、インターフェースの不正使用/時期尚早の最適化は別の問題のようであり、元の質問の精神ではありません(つまり、クラス/インターフェースがそのように構造化されていることを前提として、それを個別の[ただし有効な]懸念事項として残します)。

11
Michael

テストのみを目的としたクラスのメソッドについては、過去にmaintenance hatchという名前を目にしました。物理マシンの実際のメンテナンスハッチと同様に、これらの方法には目的がある場合があります。たとえば、レガシーコードをいくつかの年の進化の後に大きくなりすぎたときにテスト可能にする場合、メンテナンスハッチは非常に価値があります。

しかし、私はここの他の答えにも同意します。そのようなメソッドは例外であり、適切に設計されたインターフェースを使用してクラスとコンポーネントを小さく保つ場合、それらをほとんど必要としません。

26
Doc Brown

これは、このクラスをテストするためだけに追加されたもので、本番コードに組み込まれます。

これは近視眼的です...

依存関係を渡すコンストラクターはありませんjustクラスをテストするために。クラスを柔軟にするために行われます。具象Somethingへの強い依存関係を持つパラメーターなしのコンストラクターは、密結合のためにアンチパターンであり、プログラマーの便宜のためにのみ追加されます。

16
Telastyn

これは、「テストを容易にするためだけに含まれる製品コード」と呼ばれます。

よく使うならアンチパターンだと思います。それを軽減する方法は、インターフェイスに準拠し、Dependency Injectionを使用して提供される依存関係を使用してクラスを記述し、スタブとモックを使用してテスト用にクラスを分離することです。

しかし、実際には、いくつかのクラスを分離することは困難です。テスト容易性の評判にもかかわらず、ASP.NET MVCには、HttpContextのようなモックするのが難しいことで有名ないくつかの機能があります。ファイル処理を実行するように設計されたクラスは、それらをテストするために複雑なファイルとフォルダーのシナリオを設定する必要があるため、ヘルパーコードなしではテストが難しい場合があります。そのため、そのようなコードを含めることの正当な理由がわかります。

12
Robert Harvey

このために最近使用されている1つの用語は テストによる設計の損傷 です。

Telastynは正しいですが、コードスニペットは実際にはこの概念の非常に良い例ではありません。

より一般的なのは、テストが「配置」フェーズ中にクラスを特定の状態に設定することを望んでいることですが、カプセル化のため、それを直接行う方法はありません。したがって、テストでは、一連のステップを実行してオブジェクトを目的の状態にする(検証する特定のステートメントだけでなく、テストを結合する必要がある)か、カプセル化を解除するかを選択する必要があります。後者のオプションを選択するには、実稼働コードに潜在的に危険な変更を加える必要があります。

7
Ben Aaronson

私が知る限り、単体テストの目的で特別に作成された製品コードの一般的な名前はありません。 friend-assemblies (内部ユニットのユニットテストの場合)の注目すべき例外を除いて、このようなコードは通常、何かがクラスのパブリックシグネチャの形状に問題があることを示す匂いです。

ただし、この特定のの例には特定の名前があります。 "Poor Man's Dependency Injection"(または "Bastard Injection")とも呼ばれます適切な依存性注入 のように見えるため、実際には呼び出し元と依存関係。

ここのアンチパターンはそうではありません:-

public MyClass(ISomething something) { mySomething = something; }

ここでのアンチパターンは次のとおりです:-

public MyClass() { mySomething = new Something(); }

さて、ここですぐに何かが必要になるわけではありません。これを1回スライドさせてもかまいませんが、頻繁に表示される場合は、パラメーターなしのコンストラクターを削除し、MyClassが常に外部コードから依存関係を受信するようにします。 IoCコンテナまたはServiceLocator

(注:最近のIoCコンテナーはServiceLocatorsよりもファッショナブルですが、重要なことは、構成と使用法を分離することです。どちらかを使用する方がどちらも使用しないよりはましです。)


あなたの編集への応答:-

上記で述べたように、テストの困難さは通常、設計が不十分であることを示しています。これは2つの方法で修正できます。テストコードにいくつかのドアを開けて(不適切に、配置すると)、テストできないオブジェクトの内部にアクセスできるようにするか、または根本的な設計の問題を修正します。

「うまく設計されたコード」と「テストが容易なコード」の間の相関関係はかなりではないため、これは少し強力です100% 。 OPで呼び出した場合、デザインの問題は明白に見えます(オブジェクトはそれ自体の依存関係をインスタンス化します-> DIP違反ですが、より詳細に調べると依存関係が不要な場合もあります)。時々、設計の問題はあまり明白ではない、または-特にアーキテクチャのリファクタリングが困難でリスクのあるレガシーコードでは-解決する価値がない。どちらがどちらであるかを判断するには、判断を使用する必要があります。

3
Iain Galloway

テスト目的で本番コードを変更したい場合、次の2つのケースが考えられます。

  1. クラスに複雑な機能がある場合
    1. そして、あなたのオープンインターフェースはいくつかのことを組み合わせて行います
    2. 各機能を個別にテストすることはできません
    3. つまり、複合テストが失敗しても、どの部分が壊れているかはまだわかりません。
    4. はい、その機能をサブクラスに分けてテストすることは良いことです
  2. 内部実装をテストしたい場合
    1. これは、リファクタリング/カプセル化の原則にブレーキをかけます。
    2. あなたのビジネスロジックは新しい実装でブレーキをかけません
    3. しかし、テストはブレーキをかけます。テストはオブジェクトの動作をユーザーの観点から検証する必要があるため、これは悪いことです。

したがって、答えは、アンチパターン(項目2)がカプセル化の意図と目的を論理的に破るということです。 カプセル化原則の違反に投票します。

0
Riga