39

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つをスローします。」ソース

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

4

7 に答える 7

64

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

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);
}

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

于 2008-09-22T07:26:43.303 に答える
14

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

于 2009-01-16T05:40:59.507 に答える
7

単なる意見ですが、エラーテキストは次のとおりです。

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

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

複数の例外に関しては、私は C++ の世界から来ました。ここでは、大量の例外を (「throw」ステートメントごとに 1 つまで!) 大量の階層で作成することは (一般的ではないにしても) 許容されますが、.Net のメタデータ システムはおそらく許容されません。それが好きではないので、そのアドバイスです。

于 2008-09-22T06:28:54.457 に答える
4

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

于 2008-09-22T06:15:31.510 に答える
3

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

[Fact]
public void TestException()
{
   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.
}
于 2008-09-22T06:15:57.560 に答える
1

NUnit はシンプルさから遠ざかりつつあるのではないかと思いますが、どうぞ。

ExpectedException 属性に対する新しい機能拡張 (2.4.3 以降?) により、ハンドラー メソッドを介して、予期される Exception で実行されるチェックをより詳細に制御できます公式の NUnit doc ページの詳細については、ページの最後の方を参照してください。

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

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

注: ここで何かがうまくいかない.. 例外メッセージが国際化されているのはなぜですか.. 処理またはユーザーへの通知が必要なものに例外を使用していますか? 文化的に多様な開発者がバグを修正している場合を除き、これは必要ないはずです。英語または一般的に受け入れられている言語の例外で十分です。しかし、これが必要な場合に備えて..可能です:)

于 2008-09-22T06:46:01.983 に答える
0

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

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

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

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

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

私のテストコードは、次のようになります(マングリングを許してください...):

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
} 
于 2009-02-18T22:00:31.847 に答える