Assert
例外がスローされたことを確認するために(または他のTestクラスを使用して)どうすればよいですか?
Assert
例外がスローされたことを確認するために(または他のTestクラスを使用して)どうすればよいですか?
回答:
"Visual Studio Team Test"の場合、ExpectedException属性をテストのメソッドに適用しているようです。
ここのドキュメントのサンプル:Visual Studio Team Testを使用した単体テストのチュートリアル
[TestMethod]
[ExpectedException(typeof(ArgumentException),
"A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}
通常、テストフレームワークはこれに対する答えを持っています。しかし、十分に柔軟でない場合は、いつでもこれを行うことができます。
try {
somethingThatShouldThrowAnException();
Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }
@Jonasが指摘するように、これは基本例外のキャッチには機能しません。
try {
somethingThatShouldThrowAnException();
Assert.Fail(); // raises AssertionException
} catch (Exception) {
// Catches the assertion exception, and the test passes
}
どうしても例外をキャッチする必要がある場合は、Assert.Fail()を再スローする必要があります。しかし、実際には、これは手書きではいけない兆候です。テストフレームワークでオプションを確認するか、より意味のある例外をスローしてテストできるかどうかを確認します。
catch (AssertionException) { throw; }
キャッチする例外の種類の指定を含め、このアプローチを好きなように適用できるはずです。特定のタイプのみが必要な場合は、次のようにcatch
ブロックを終了します。
} catch (GoodException) {
} catch (Exception) {
// not the right kind of exception
Assert.Fail();
}
これを実装するための私の好ましい方法は、Throwsと呼ばれるメソッドを作成し、他のAssertメソッドと同じように使用することです。残念ながら、.NETでは静的拡張メソッドを作成できないため、このメソッドをAssertクラスのビルドに実際に属しているかのように使用することはできません。MyAssertと呼ばれる別のものまたは類似のものを作成するだけです。クラスは次のようになります。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests
{
public static class MyAssert
{
public static void Throws<T>( Action func ) where T : Exception
{
var exceptionThrown = false;
try
{
func.Invoke();
}
catch ( T )
{
exceptionThrown = true;
}
if ( !exceptionThrown )
{
throw new AssertFailedException(
String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
);
}
}
}
}
つまり、単体テストは次のようになります。
[TestMethod()]
public void ExceptionTest()
{
String testStr = null;
MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}
これは、他の単体テスト構文と非常によく似た動作をします。
Assert.ThrowsException<T>
と-blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/…をAssert.ThrowsExceptionAsync<T>
参照
NUNITを使用する場合、次のようなことができます。
Assert.Throws<ExpectedException>(() => methodToTest());
さらに検証するために、スローされた例外を保存することもできます。
ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);
最初はExpectedException
属性を持っていなかったMSTestを使用している場合、これを行うことができます。
try
{
SomeExceptionThrowingMethod()
Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
Assert.IsTrue(ex is SpecificExceptionType);
}
ExpectedExceptionの使用には注意が必要です。ここに示すように、いくつかの落とし穴につながる可能性があります。
http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx
そしてここ:
http://xunit.github.io/docs/comparisons.html
例外をテストする必要がある場合は、方法に眉をひそめることが少なくなります。try {act / fail} catch {assert}メソッドを使用できます。これは、ExpectedException以外の例外テストを直接サポートしていないフレームワークに役立ちます。
より良い代替手段は、他のすべての間違いから学び、改善された、非常に近代的で将来を見据えた拡張可能な単体テストフレームワークであるxUnit.NETを使用することです。そのような改善の1つはAssert.Throwsで、これは例外をアサートするためのはるかに優れた構文を提供します。
あなたはgithubのでxUnit.NETを見つけることができます。http://xunit.github.io/
MSTest(v2)には、次のように使用できるAssert.ThrowsException関数があります。
Assert.ThrowsException<System.FormatException>(() =>
{
Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
});
nugetでインストールできます: Install-Package MSTest.TestFramework
私が取り組んでいるプロジェクトでは、これを行う別のソリューションがあります。
まず、例外の原因となったメソッド呼び出しを考慮に入れるため、ExpectedExceptionAttributeは好きではありません。
代わりにヘルパーメソッドを使用してこれを行います。
テスト
[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
var file = File.Create("Accounts.bin");
file.WriteByte(1);
file.Close();
IAccountRepository repo = new FileAccountRepository();
TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());
}
HelperMethod
public static TException AssertThrows<TException>(Action action) where TException : Exception
{
try
{
action();
}
catch (TException ex)
{
return ex;
}
Assert.Fail("Expected exception was not thrown");
return null;
}
きちんと、そうではありません;)
次を使用してNugetからパッケージをダウンロードできます。PM > Assert.Throws()を追加するInstall-Package MSTestExtensions MSTESTにNUnitの/ xUnitフレームのスタイルで構文を。
高レベルの手順:アセンブリをダウンロードしてBaseTestから継承すると、Assert.Throws()を使用できます構文をます。
Throws実装のメインメソッドは次のようになります。
public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
try
{
task();
}
catch (Exception ex)
{
AssertExceptionType<T>(ex);
AssertExceptionMessage(ex, expectedMessage, options);
return;
}
if (typeof(T).Equals(new Exception().GetType()))
{
Assert.Fail("Expected exception but no exception was thrown.");
}
else
{
Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
}
}
開示:このパッケージをまとめました。
詳細:http : //www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html
簡単な1行でこれを実現できます。
操作foo.bar()
が非同期の場合:
await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());
foo.bar()
非同期でない場合
Assert.ThrowsException<Exception>(() => foo.bar());
ArgumentException
forをトラップしません。テストする高度な基準がある場合は、古いTry Catchを使用して例外応答をテストすることをお勧めしますが、多くの場合、これは非常に役立ちます。
ExpectedException属性の使用(制約が多すぎてエラーが発生しやすいため)や、各テストでtry / catchブロックを作成すること(複雑すぎてエラーが発生しやすいため)はお勧めしません。適切に設計されたアサートメソッドを使用します-テストフレームワークによって提供されるか、独自に作成します。これが私が書いて使用したものです。
public static class ExceptionAssert
{
private static T GetException<T>(Action action, string message="") where T : Exception
{
try
{
action();
}
catch (T exception)
{
return exception;
}
throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated. " + message);
}
public static void Propagates<T>(Action action) where T : Exception
{
Propagates<T>(action, "");
}
public static void Propagates<T>(Action action, string message) where T : Exception
{
GetException<T>(action, message);
}
public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
{
Propagates(action, validation, "");
}
public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
{
validation(GetException<T>(action, message));
}
}
使用例:
[TestMethod]
public void Run_PropagatesWin32Exception_ForInvalidExeFile()
{
(test setup that might propagate Win32Exception)
ExceptionAssert.Propagates<Win32Exception>(
() => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
(more asserts or something)
}
[TestMethod]
public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
{
(test setup that might propagate FileNotFoundException)
ExceptionAssert.Propagates<FileNotFoundException>(
() => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
e => StringAssert.Contains(e.Message, "NotThere.exe"));
(more asserts or something)
}
ノート
検証コールバックをサポートする代わりに例外を返すことは、そうすることでこのアサートの呼び出し構文が、私が使用する他のアサートと大きく異なることを除いて、理にかなったアイデアです。
他とは異なり、呼び出しから例外が伝播するかどうかのみをテストできるため、「スロー」ではなく「伝播」を使用します。例外がスローされることを直接テストすることはできません。しかし、私はあなたがスローをイメージすることができると思います:スローされ、キャッチされません。
最終的な思考
この種のアプローチに切り替える前に、テストで例外タイプのみを検証する場合はExpectedException属性を使用し、さらに検証が必要な場合はtry / catchブロックを使用することを検討しました。しかし、各テストにどの手法を使用するかを考える必要があるだけでなく、ニーズの変化に応じてコードを1つの手法から別の手法に変更するのは簡単なことではありませんでした。1つの一貫したアプローチを使用すると、精神的な労力を節約できます。
したがって、要約すると、このアプローチは、使いやすさ、柔軟性、堅牢性(間違いを犯しにくい)を備えています。
上記の@Richibanによって提供されるヘルパーは、例外がスローされる状況を処理しないが、期待されるタイプを処理しないことを除いて、うまく機能します。次のことについて説明します。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests
{
public static class MyAssert
{
/// <summary>
/// Helper for Asserting that a function throws an exception of a particular type.
/// </summary>
public static void Throws<T>( Action func ) where T : Exception
{
Exception exceptionOther = null;
var exceptionThrown = false;
try
{
func.Invoke();
}
catch ( T )
{
exceptionThrown = true;
}
catch (Exception e) {
exceptionOther = e;
}
if ( !exceptionThrown )
{
if (exceptionOther != null) {
throw new AssertFailedException(
String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
exceptionOther
);
}
throw new AssertFailedException(
String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
);
}
}
}
}
他のテストクラスの使用について言及しているので、ExpectedException
属性よりも優れたオプションは、ShoudlyのShould.Throwを使用することです。
Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });
注文を作成するには、顧客が住所を持っている必要があるとしましょう。そうでない場合、メソッドの結果はになります。次に、次のように書きます。CreateOrderForCustomer
ArgumentException
[TestMethod]
public void NullUserIdInConstructor()
{
var customer = new Customer(name := "Justin", address := null};
Should.Throw<ArgumentException>(() => {
var order = CreateOrderForCustomer(customer) });
}
これは、 ExpectedException
エラーをスローする対象を特定しているため属性ています。これにより、テストの要件が明確になり、テストが失敗した場合の診断も容易になります。
Should.ThrowAsync
非同期メソッドのテストもあることに注意してください。
まあ私はここで他の皆が以前に言ったことをかなり要約します...とにかく、ここに私が良い答えに従って作成したコードがあります:)すべてはコピーと使用だけです...
/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
try
{
methodToExecute();
}
catch (TException) {
return;
}
catch (System.Exception ex)
{
Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
}
Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
}
次の例については、nUnit Docsをご覧ください。
[ExpectedException( typeof( ArgumentException ) )]
NUnitを使用する場合は、次のことを試してください。
Assert.That(() =>
{
Your_Method_To_Test();
}, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));
これは古い質問ですが、ディスカッションに新しい考えを付け加えたいと思います。Arrange、Act、AssertパターンをExpected、Arrange、Act、Assertに拡張しました。予期される例外ポインタを作成し、それが割り当てられていることを表明できます。これは、assertsをcatchブロックで実行するよりもクリーンに感じられ、Actセクションは主にコードの1行だけでテスト中のメソッドを呼び出すことができます。また、コード内の複数のポイントに出入りするAssert.Fail();
必要もありませんreturn
。その他の例外がスローされると、テストが失敗します。これは、テストがキャッチされないためです。また、予期したタイプの例外がスローされたが、それが予期したものではなかった場合、メッセージまたは他のプロパティに対するアサート例外は、テストが不注意でパスしないようにするのに役立ちます。
[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
// Expectations
InvalidOperationException expectedException = null;
string expectedExceptionMessage = "Bar did something invalid.";
// Arrange
IDependency dependency = DependencyMocks.Create();
Foo foo = new Foo(dependency);
// Act
try
{
foo.Bar();
}
catch (InvalidOperationException ex)
{
expectedException = ex;
}
// Assert
Assert.IsNotNull(expectedException);
Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}