web-dev-qa-db-ja.com

voidメソッドの単体テスト?

何も返さないメソッドを単体テストする最良の方法は何ですか?特にc#で。

私が実際にテストしようとしているのは、ログファイルを取得して特定の文字列を解析するメソッドです。その後、文字列はデータベースに挿入されます。これまでに行われたことはありませんが、TDDが非常に新しいので、これをテストできるのか、それとも実際にテストされないのか疑問に思っています。

150
jdiaz

メソッドが何も返さない場合は、次のいずれかです

  • 命令的-あなたはオブジェクトにそれ自身に何かをするように頼んでいる。例えば、状態を変更する(確認を期待せずに..完了)
  • informational-誰かに何かが起こったことを(アクションや応答を期待せずに)通知するだけです。

必須の方法-タスクが実際に実行されたかどうかを確認できます。状態の変更が実際に行われたかどうかを確認します。例えば.

void DeductFromBalance( dAmount ) 

このメッセージを投稿した残高が実際にdAmountによる初期値よりも小さいかどうかを確認することでテストできます。

情報メソッド-オブジェクトのパブリックインターフェイスのメンバーとしてはまれであるため、通常は単体テストされていません。ただし、必要に応じて、通知に対して行われる処理が行われるかどうかを確認できます。例えば.

void OnAccountDebit( dAmount )  // emails account holder with info

電子メールが送信されているかどうかを確認することでテストできます

実際の方法に関する詳細を投稿すれば、人々はよりよく答えることができます。
Update:メソッドは2つのことを実行しています。実際には、2つのメソッドに分割し、個別にテストできるようにしました。

string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );

String []は、ダミーファイルと予想される文字列を最初のメソッドに提供することで簡単に検証できます。 2つ目はややトリッキーです。モック(Googleまたはモックフレームワークの検索スタックオーバーフロー)を使用してDBを模倣するか、実際のDBをヒットして、文字列が正しい場所に挿入されたかどうかを確認できます。 このスレッド をチェックして、良い本を探してください。もしあなたが困っているなら、プラグマティックユニットテストをお勧めします。
コードでは次のように使用されます

InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );
139
Gishu

その副作用をテストします。これも:

  • 例外をスローしますか? (もしそうなら、それを確認してください。もしそうでないなら、あなたが注意していないかもしれないいくつかのコーナーケースを試してください-null引数は最も明白なものです。)
  • そのパラメーターでうまく動作しますか? (それらが可変であれば、そうすべきではないときにそれらを突然変異させますか?
  • 呼び出しているオブジェクト/タイプの状態に適切な効果がありますか?

もちろん、テストの方法muchには制限があります。たとえば、通常、可能なすべての入力でテストすることはできません。実用的なテスト-コードが適切に設計され、正しく実装されていることを確信できるほど十分であり、呼び出し元が期待する内容の補足ドキュメントとして機能するのに十分です。

59
Jon Skeet

いつものように、メソッドが何をすべきかをテストします!

どこかでグローバル状態を変更する必要がありますか(うーん、コードの匂い!)

インターフェイスを呼び出す必要がありますか?

間違ったパラメーターで呼び出されたときに例外をスローする必要がありますか?

適切なパラメーターで呼び出されたときに例外をスローする必要はありませんか?

それは...?

28
David Schmitt

無効な戻り値のタイプ/サブルーチンは古いニュースです。 Void戻り値型を作成していません(非常に怠けている場合を除いて)約8年(この答えの時から、この質問が聞かれる少し前)。

次のようなメソッドの代わりに:

public void SendEmailToCustomer()

Microsoftのint.TryParse()パラダイムに従うメソッドを作成します。

public bool TrySendEmailToCustomer()

長期的に使用するためにメソッドが返す必要のある情報はないかもしれませんが、メソッドがジョブを実行した後にメソッドの状態を返すことは、呼び出し側にとって大きな用途です。

また、boolが唯一の状態タイプではありません。以前に作成されたサブルーチンが実際に3つ以上の異なる状態(正常、正常、不良など)を返す場合があります。それらの場合、あなたはただ使うだろう

public StateEnum TrySendEmailToCustomer()

ただし、Try-Paradigmは、ボイドリターンをテストする方法に関するこの質問にある程度答えていますが、他の考慮事項もあります。たとえば、「TDD」サイクル中または後に、「リファクタリング」し、メソッドで2つのことを実行していることに気付くでしょう...したがって、「単一責任原則」を破ります。そのため、最初に注意する必要があります。第二に、依存関係を明らかにしたかもしれません...「永続的な」データに触れています。

質問方法でデータアクセスを行う場合は、n層またはn層のアーキテクチャにリファクタリングする必要があります。しかし、「その後、文字列がデータベースに挿入される」と言うとき、実際にはビジネスロジックレイヤーまたは何かを呼び出していることを意味すると想定できます。ええ、それを仮定します。

オブジェクトがインスタンス化されると、オブジェクトに依存関係があることがわかります。これは、オブジェクトまたはメソッドで依存性注入を行うかどうかを決定する必要がある場合です。つまり、コンストラクターまたは問題のメソッドには新しいパラメーターが必要です。

public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)

ビジネス/データ層オブジェクトのインターフェースを受け入れることができるようになったので、ユニットテスト中にそれをモックアウトし、依存関係や「偶発的な」統合テストを恐れることはありません。

したがって、ライブコードでは、REAL IBusinessDataEtcオブジェクトを渡します。ただし、単体テストでは、MOCK IBusinessDataEtcオブジェクトを渡します。そのモックでは、int XMethodWasCalledCountなどの非インターフェイスプロパティ、またはインターフェイスメソッドが呼び出されたときに状態が更新されるものを含めることができます。

そのため、ユニットテストはMethod-In-Questionを通過し、それらのロジックを実行し、IBusinessDataEtcオブジェクト内の1つまたは2つのメソッドまたは選択されたメソッドセットを呼び出します。ユニットテストの最後にアサーションを実行すると、テストすることがいくつかあります。

  1. 現在はTry-Paradigmメソッドである「サブルーチン」の状態。
  2. モックIBusinessDataEtcオブジェクトの状態。

構築レベルでの依存性注入のアイデアの詳細については、ユニットテストに関係しているため、Builderの設計パターンをご覧ください。現在のインターフェイス/クラスごとにもう1つのインターフェイスとクラスが追加されますが、これらは非常に小さく、ユニットテストを改善するために機能が大幅に強化されています。

10
Suamere

オブジェクトに何らかの効果があります。..効果の結果を照会します。目に見える効果がない場合、単体テストの価値はありません!

5
Keith Nicholas

おそらく、メソッドは何かをし、単純に戻るのではないでしょうか?

これが事実であると仮定すると、次のようになります。

  1. 所有者オブジェクトの状態を変更する場合、状態が正しく変更されたことをテストする必要があります。
  2. オブジェクトをパラメーターとして受け取り、そのオブジェクトを変更する場合、オブジェクトが正しく変更されていることをテストする必要があります。
  3. 特定のケースで例外がスローされる場合、それらの例外が正しくスローされることをテストします。
  4. 自身のオブジェクトまたは他のオブジェクトの状態に基づいて動作が異なる場合は、状態を事前設定し、上記の3つのテストメソッドのいずれかを通じてメソッドに正しいIがあることをテストします。

あなたがメソッドが何をするかを私たちに知らせてくれれば、私はより具体的になるかもしれません。

4
David Arno

これを試して:

[TestMethod]
public void TestSomething()
{
    try
    {
        YourMethodCall();
        Assert.IsTrue(true);
    }
    catch {
        Assert.IsTrue(false);
    }
}
4
Nathan Alard

Rhino Mocks を使用して、予想される呼び出し、アクション、および例外を設定します。メソッドの一部をモックまたはスタブアウトできると仮定します。ここでは、メソッドやコンテキストについての詳細を知らずに知ることは困難です。

3
dove

それが何をしているかに依存します。パラメーターがある場合は、正しいパラメーターセットで呼び出されたかどうかを後で確認できるモックを渡します。

2
André

次の方法でも試すことができます。

[TestMethod]
public void ReadFiles()
{
    try
    {
        Read();
        return; // indicates success
    }
    catch (Exception ex)
    {
        Assert.Fail(ex.Message);
    }
}
2
Reyan Chougle

Voidメソッドを呼び出すためにどのインスタンスを使用している場合でも、使用できるのは、Verfiy

例えば:

私の場合、その_Logはインスタンスであり、LogMessageはテストするメソッドです。

try
{
    this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure");
}
Catch 
{
    Assert.IsFalse(ex is Moq.MockException);
}

Verifyは、テストが失敗するメソッドの失敗により例外をスローしますか?

0