[英]Nunit async test exception assertion
[編輯(2020 年 5 月)] - 據報道,此問題已在 NUnit 的較新版本中得到解決。 請參閱Nunit.ThrowsAsync 。 (參考這個答案,謝謝@James-Ross)
我有一個帶有此操作的控制器UserController
// GET /blah
public Task<User> Get(string domainUserName)
{
if (string.IsNullOrEmpty(domainUserName))
{
throw new ArgumentException("No username specified.");
}
return Task.Factory.StartNew(
() =>
{
var user = userRepository.GetByUserName(domainUserName);
if (user != null)
{
return user;
}
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("{0} - username does not exist", domainUserName)));
});
}
我正在嘗試為拋出 404 異常的情況編寫測試。
這是我嘗試過的,輸出 -
1)
[Test]
public void someTest()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
Assert.That(async () => await userController.Get("foo"), Throws.InstanceOf<HttpResponseException>());
}
結果測試失敗
Expected: instance of <System.Web.Http.HttpResponseException>
But was: no exception thrown
[測試] public void someTest() { var mockUserRepository = new Mock(); mockUserRepository.Setup(x => x.GetByUserName(It.IsAny())).Returns(default(User)); var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var httpResponseException = Assert.Throws<HttpResponseException>(() => userController.Get("foo").Wait()); Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
結果測試失敗
Expected: <System.Web.Http.HttpResponseException>
But was: <System.AggregateException> (One or more errors occurred.)
[Test]
public void someTest()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var httpResponseException = Assert.Throws<HttpResponseException>(async () => await userController.Get("foo"));
Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
結果測試失敗
Expected: <System.Web.Http.HttpResponseException>
But was: null
[Test]
[ExpectedException(typeof(HttpResponseException))]
public async void ShouldThrow404WhenNotFound()
{ var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var task = await userController.Get("foo");
}
結果測試通過
問題 -
對這些行為及其原因的任何比較都會很棒!
由於async void
您看到了問題。
特別是:
async () => await userController.Get("foo")
被轉換為TestDelegate
,它返回void
,因此您的 lambda 表達式被視為async void
。 因此,測試運行器將開始執行 lambda,但不會等待它完成。 lambda 在Get
完成之前返回(因為它是async
),並且測試運行程序看到它無異常地返回。
Wait
將所有異常包裝在AggregateException
。
同樣, async
lambda 被視為async void
,因此測試運行程序不會等待其完成。
我建議您創建此async Task
而不是async void
,但在這種情況下,測試運行程序確實等待完成,因此會看到異常。
根據這個錯誤報告,在 NUnit 的下一個版本中會有一個修復程序。 同時,您可以構建自己的ThrowsAsync
方法; xUnit 的一個例子是here 。
我不確定它是何時添加的,但當前版本的 Nunit(撰寫本文時為 3.4.1)包含一個 ThrowsAsync 方法
見https://github.com/nunit/docs/wiki/Assert.ThrowAsync
例子:
[Test]
public void ShouldThrow404WhenNotFound()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var exception = Assert.ThrowsAsync<HttpResponseException>(() => userController.Get("foo"));
Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
這個博客討論了與我類似的問題。
我遵循了那里提出的建議,並進行了這樣的測試-
[Test]
public void ShouldThrow404WhenNotFound()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var aggregateException = Assert.Throws<AggregateException>(() => userController.Get("foo").Wait());
var httpResponseException = aggregateException.InnerExceptions
.FirstOrDefault(x => x.GetType() == typeof(HttpResponseException)) as HttpResponseException;
Assert.That(httpResponseException, Is.Not.Null);
Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
我對它不太滿意,但這有效。
編輯 1
受@StephenCleary 的啟發,我添加了一個靜態幫助器類來執行我正在尋找的斷言。 看起來是這樣的——
public static class AssertEx
{
public static async Task ThrowsAsync<TException>(Func<Task> func) where TException : class
{
await ThrowsAsync<TException>(func, exception => { });
}
public static async Task ThrowsAsync<TException>(Func<Task> func, Action<TException> action) where TException : class
{
var exception = default(TException);
var expected = typeof(TException);
Type actual = null;
try
{
await func();
}
catch (Exception e)
{
exception = e as TException;
actual = e.GetType();
}
Assert.AreEqual(expected, actual);
action(exception);
}
}
我現在可以進行類似的測試 -
[Test]
public async void ShouldThrow404WhenNotFound()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
Action<HttpResponseException> asserts = exception => Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
await AssertEx.ThrowsAsync(() => userController.Get("foo"), asserts);
}
如果您等待任務,則拋出的異常將聚合到 AggregateException 中。 您可以檢查 AggregateException 的內部異常。 這可能是您的案例 2 不起作用的原因。
由在任務內運行的用戶代碼引發的未處理異常將傳播回加入線程,但在本主題后面描述的某些情況下除外。 當您使用靜態或實例 Task.Wait 或 Task.Wait 方法之一並通過將調用包含在 try-catch 語句中來處理它們時,會傳播異常。 如果任務是附加子任務的父任務,或者您正在等待多個任務,則可能會引發多個異常。 為了將所有異常傳播回調用線程,Task 基礎結構將它們包裝在一個 AggregateException 實例中。 AggregateException 有一個 InnerExceptions 屬性,可以枚舉它來檢查所有拋出的原始異常,並單獨處理(或不處理)每個異常。 即使只拋出一個異常,它仍然包含在一個 AggregateException 中。
我有一個類似的問題,你在場景 3 測試用例由於以下結果失敗
Expected: <UserDefineException>
But was: null
通過使用 Assert.ThrowAsync<> 問題得到解決
我的 Web API 操作方法和單元測試用例方法如下
public async Task<IHttpActionResult> ActionMethod(RequestModel requestModel)
{
throw UserDefineException();
}
[Test]
public void Test_Contrller_Method()
{
Assert.ThrowsAsync<UserDefineException>(() => _controller.ActionMethod(new RequestModel()));
}
這是文檔中的一個示例:
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await MethodThatThrows());
ThrowsAsync
async
/ await
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.