3

クラスをテストしようとしています

public class Parser
{

    private static IDictionary<String, Regex> PhrasesToRegexp { get; set; }

    public static void InitPhrases(IList<String> Phrases, Boolean useDeclination )
    {
        throw new NotImplementedException();
    }

    ...

    public ParsingResults Find(String source)
    {
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(source);
        return new ParsingResults(FindUrls(doc), CountPhrases(doc));
    }



    private IList<String> FindUrls(HtmlDocument source)
    {
        return source.DocumentNode.SelectNodes("//a[@href]").
            Select(link => link.GetAttributeValue("href", "")).ToList();
    }

    private IDictionary<String, int> CountPhrases(HtmlDocument source)
    {
        IDictionary<String, int> results = new Dictionary<String, int>();
        foreach (String key in PhrasesToRegexp.Keys)
        {
            results.Add( key , 0 );
        }

        foreach (HtmlNode node in source.DocumentNode.SelectNodes("//p"))
        {
            foreach (String phrase in results.Keys)
            {
                results[phrase] += PhrasesToRegexp[phrase].Matches
                    (Regex.Replace(node.InnerText, @"<(.|\n)*?>", string.Empty)).Count;
            }
        }
        return results;
    }

}

問題は、プロパティPhrasesToRegexpが初期化される (される) ことでInitPhrasesあり、Find メソッドの単体テストを記述しようとしています。基本的に、このプライベート プロパティの値を設定する必要がありますPhrasesToRegexp。それを行う方法はありますか?私はモックの専門家ではありませんが、このプロパティとテストされたメソッドが同じオブジェクトにあるため、モックはうまくいかないと思います。

4

5 に答える 5

1

単体テスト専用の新しいコンストラクターを追加することもできますが、クラスへの変更を最小限に抑えて単体テスト可能にすることをお勧めします。単体テストをサポートするための専門化は、通常、最終的なアプリケーションで実行される実際のコードをテストしていないことを意味します。専門化すればするほど、実際のコードが完全にテストされていることを確認するのが難しくなり、テスト対象のコードに不要な副作用が発生する可能性が高くなります。

代わりに、(可能であれば)クライアントとしてクラスを使用しようとします-インスタンスを構築し、クライアントとしてメソッドを呼び出す場合、プライベート状態で突く必要はなく、単体テストは正確にテストされますクライアントコードが使用するもの。また、コードの変更との同期を維持することを忘れることができる単体テストの特別な経路がないため、クラスの内部動作を変更した場合でも、テストは有効/有効であり続ける可能性が高くなります。

プロパティを公開して直接製品化する場合は、プロパティを変更してinternal使用InternalsVisibleToするのが標準的なアプローチですが、すべての人にとって永続的に内部化されるため、カプセル化の感覚を損なうことになります。別のプログラマーは、あなたが「ねえ、私たちは良い友達です。好きなだけ私の内部状態でパーティーをしてください」ではなく、「テストのための内部」を意味していることをどのように知っていますか。ユニットテストをしたいときにそれを捨てるだけなら、そこには何がプライベートなのですか?したがって、コードをプライベートに保つ別のアプローチは、単体テスト用の特別なビルドを使用することです。これは、通常のビルドではプライベートのままにして、テスト用にアクセスするプライベートを公開できるように#defineを設定します。

1つの方法は、プロパティ自体に対するブルートフォースです(ただし、これは非常に厄介な場合があります)。

#if UNIT_TEST
    public
#else
    private
#endif
int MyPrivateProperty { get; set; }

または、よりクリーンなアプローチ(ただし、より多くの作業)は、元のコードを無傷のままにしてアクセスメソッドを追加し、テスト対象のコードを誤って破損/変更する可能性を最小限に抑えることです。

private int MyProperty { get; set; }

#if UNIT_TEST
    public int AccessMyProperty
    {
        get { return(MyProperty); }
        set { MyProperty = value; }
    }
#endif 
于 2013-01-06T23:41:34.410 に答える
0

1)代わりに、privateとして宣言する必要がありますinternal

2)AssemblyInfo.csでアセンブリを追加する必要があります。そこから、次のようにクラスにアクセスします。

[assembly: InternalsVisibleTo("MainFormUnitTests")]
于 2013-01-06T22:59:34.653 に答える
0

私にとって、この問題は、パーサーで 2 つの異なる懸念を表現しているため発生しています。

  1. フレーズと正規表現のクイック リファレンス キャッシュ
  2. 解析ロジック

これら 2 つの懸念事項を 2 つの異なるクラスに分割することをお勧めします。さらに、ある時点で静的辞書のメモリをクリアする (またはキャッシュを無効にする) ことに対処したい場合があります。

インターフェイスの背後にあるキャッシュ ロジックを取り出すとICachePhrasesAndRegex、テスト用に依存関係を簡単にモックできます。

于 2013-01-06T23:26:14.490 に答える
0

これは、使用しているモック フレームワークによっては可能な場合があります。私は通常 Moq または NMock2 を使用します。その場合、実行可能なオプションは次のとおりです。

1)プロパティをプライベートではなく内部にします。これにより、単体テストで設定できます。単体テストが別のプロジェクトにある場合は、InternalsVisibleTo 属性を使用する必要がある場合があります。

2) PhrasesToRegexp を受け入れる単体テスト用の別のコンストラクターを作成します。

于 2013-01-06T22:58:25.920 に答える