web-dev-qa-db-ja.com

Visual Studio Testのリソースファイルからの特定の例外メッセージを使用して、予期される例外をテストするにはどうすればよいですか?

Visual Studio Testは、ExpectedException属性を使用して、予期される例外をチェックできます。次のような例外を渡すことができます。

[TestMethod]
[ExpectedException(typeof(CriticalException))]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

次のように、ExpectedException内に含まれるメッセージを確認することもできます。

[TestMethod]
[ExpectedException(typeof(CriticalException), "An error occured")]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

しかし、I18Nアプリケーションをテストするときは、リソースファイルを使用してそのエラーメッセージを取得します(必要に応じて、エラーメッセージの異なるローカライズをテストすることもできますが、Visual Studioではこれを実行できません。

[TestMethod]
[ExpectedException(typeof(CriticalException), MyRes.MultipleOrganisationsNotAllowed)]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

コンパイラは次のエラーを出します:

属性引数は、定数式、typeof式、または属性の配列作成式である必要があります

誰かがリソースファイルからのメッセージを持つ例外をテストする方法を知っていますか?


私が検討した1つのオプションは、カスタム例外クラスを使用することですが、次のようなよく耳にするアドバイスに基づいています。

「他の既存の例外とは異なる方法でプログラムで処理できるエラー条件がある場合は、カスタム例外を作成してスローします。それ以外の場合は、既存の例外の1つをスローします。」 ソース

私は通常のフローで例外を異なる方法で処理することを期待していません(それは重大な例外なので、とにかくパニックモードに入ります)、各テストケースに対して例外を作成することは正しいことだとは思いません。意見はありますか?

39
Alex Duggleby

ちょうど意見ですが、エラーテキストは次のようになります。

  • はテストの一部であり、その場合、リソースからの取得は「間違った」ものになります(そうしないと、リソースが一貫して破損する可能性があります)。したがって、リソースを変更したときにテストを更新してください(またはテストが失敗します)。
  • はテストの一部ではないため、例外がスローされることだけを気にする必要があります。

ロケールで実行できる場合、最初のオプションでは複数の言語をテストできることに注意してください。

複数の例外については、私はC++の出身です。大きな相続人で(「throw」ステートメントごとに1つのポイントまで)大量の例外を作成することは(一般的ではないにしても)許容されますが、.Netのメタデータシステムはおそらくそれは好きではないので、そのアドバイス。

7
Simon Buchan

属性の代わりにヘルパーメソッドを使用することをお勧めします。このようなもの:

public static class ExceptionAssert
{
  public static T Throws<T>(Action action) where T : Exception
  {
    try
    {
      action();
    }
    catch (T ex)
    {
      return ex;
    }
    Assert.Fail("Exception of type {0} should be thrown.", typeof(T));

    //  The compiler doesn't know that Assert.Fail
    //  will always throw an exception
    return null;
  }
}

次に、次のようなテストを記述できます。

[TestMethod]
public void GetOrganisation_MultipleOrganisations_ThrowsException()
{
  OrganizationList organizations = new Organizations();
  organizations.Add(new Organization());
  organizations.Add(new Organization());

  var ex = ExceptionAssert.Throws<CriticalException>(
              () => organizations.GetOrganization());
  Assert.AreEqual(MyRes.MultipleOrganisationsNotAllowed, ex.Message);
}

これには、テストメソッド内のどこかではなく、例外がスローされると予想していた行に例外がスローされることを検証するという利点もあります。

63
Daniel Plaisted

ExpectedException Message引数は、例外のメッセージと一致しません。むしろ、予想される例外が実際に発生しなかった場合にテスト結果に出力されるメッセージです。

14
user53794

ExpectedException属性を使用する代わりに、テストコードで明示的なtry-catchを実行できるだけだと思います。次に、リソースファイルを読み取り、キャッチされた例外に伴うエラーメッセージと比較するヘルパーメソッドを考え出すことができます。 (もちろん、例外がなければ、テストケースは不合格と見なされます)

4
cruizer

非常に素晴らしい xUnit.Net テストライブラリを使用するように切り替える場合、[ExpectedException]を次のようなものに置き換えることができます。

[Fact]
public void TestException()
{
   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.
}
3
Jedidja

NUnitは単純さから遠ざかっていますか...

ExpectedException属性の新しい拡張機能(2.4.3以降?)により、ハンドラーメソッドを介して、予期される例外に対して実行されるチェックをより詳細に制御できます公式NUnitドキュメントページ ..の詳細については、ページの終わりに向かってください。

[ExpectedException( Handler="HandlerMethod" )]
public void TestMethod()
{
...
}

public void HandlerMethod( System.Exception ex )
{
...
}

注:何かがここでは感じられません..例外メッセージが国際化されているのはなぜですか?処理またはユーザーへの通知が必要なものに例外を使用していますか?あなたがバグを修正する文化的に多様な開発者の束を持っていない限り、あなたはこれを必要とすべきではありません。英語または一般的に認められている言語の例外で十分です。しかし、あなたがこれを持っている必要がある場合..それは可能です:)

1
Gishu

私は自分で同様の問題を解決しようとしているときにこの質問に出くわしました。 (以下で解決した解決策について詳しく説明します。)

例外メッセージをコードのにおいとして国際化することについてのギシュのコメントに同意する必要があります。

私はこれを最初に自分のプロジェクトで実行したので、アプリケーションによってスローされたエラーメッセージとユニットテストの間で一貫性を保つことができました。つまり、例外メッセージを1か所で定義するだけで済みますが、リソースファイルは、さまざまなラベルや文字列に既に使用していたため(参照を追加する意味があったため)、これを行うには賢明な場所のように見えましたこれらの同じラベルが適切な場所に表示されていることを確認するために、テストコードでそれを確認します。

ある時点で、ExpectedException属性による定数の要件を回避するためにtry/catchブロックを使用することを検討(およびテスト)しましたが、これを大規模に適用すると、非常に多くの追加コードが発生するように思われました。

最後に、私が解決した解決策は、リソースライブラリに静的クラスを作成し、その中に例外メッセージを格納することでした。このように、それらを国際化する必要はなく(私が同意することは意味がありません)、それらは同じ名前空間にあるため、リソース文字列にアクセスできる場合はいつでもアクセス可能になります。 (これは、例外テキストの検証を複雑なプロセスにしないようにしたいという私の望みと一致します。)

次に、私のテストコードは単純に要約されます(マングリングを許して...):

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
} 
0
Peter Bernier