使用 Nunit 在 ASP MVC 控制器中测试异步方法
Testing an async method in ASP MVC Controller with Nunit
我有一个 ASP.NET MVC 应用程序,其控制器具有异步方法,返回 Task<PartialViewResult>
对象并标有 async 关键字。
该方法仅以异步方式从数据库中获取数据。
public async Task<PartialViewResult> SomeMethod()
{
using (var unitOfWork = _factory.Create())
{
var result = await unitOfWork.SomeRepository.GetAsync();
return PartialView(result);
};
}
在测试期间,流只是冻结在这个位置(在 运行 时此代码运行良好):
var models = await unitOfWork.SomeRepository.GetAsync();
这是我对这个方法的测试:
public void GetExchange_GetView_OkModelIsViewModel()
{
//fake Repository returns fake Data from DB
var mockSomeRepository = new Mock<ISomeRepository>();
mockSomeRepository.Setup(x => x.GetAsync(...).Returns(new Task<List<SomeType>>(() => new List<SomeType>()));
//fake UoW returns fake Repository
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.SomeRepository).Returns(mockSomeRepository.Object);
//fake factory create fake UoW
var fakeUnitOfWorkFactory = new Mock<UnitOfWorkFactory>();
fakeUnitOfWorkFactory.Setup(x => x.Create()).Returns(mockUnitOfWork.Object);
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory);
//Our async method
var result = controller.SomeMethod();
result.Wait();
//---Assert--
}
问题:为什么我的方法中的流在测试执行期间冻结???
更新
如果我替换
,此测试将开始工作
var result = await unitOfWork.SomeRepository.GetAsync();
到
var models = unitOfWork.SomeRepository.GetAsync();
models.Start();
models.Wait();
var result = models.Result;
但我不太明白为什么会这样。有人可以解释一下吗?
测试异步方法时,您的测试方法也应该是异步的。 NUnit 可以毫无问题地处理这个问题。
[Test]
public async Task GetExchange_GetView_OkModelIsViewModel() {
// ...
var controller = new SomeController(fakeUnitOfWorkFactory);
var result = await controller.SomeMethod(); // call using await
// ...
}
why is the stream in my method freezes during test execution?
测试有一些问题。
最初的示例是将阻塞调用 (.Wait()
) 与导致死锁的异步调用混合在一起,从而导致挂起(死锁)。
测试应该一直转换为异步。测试运行器应该能够毫无问题地处理它。
public async Task GetExchange_GetView_OkModelIsViewModel() { ... }
接下来 GetAsync
方法的设置没有正确完成。
因为该方法未配置为 return 允许代码继续执行的已完成任务,这也会导致该任务阻塞
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake Repository returns fake Data from DB
var mockSomeRepository = new Mock<ISomeRepository>();
mockSomeRepository
.Setup(x => x.GetAsync())
.Returns(Task.FromResult(fakeData)); // <-- note the correction here
根据测试需要,设置可以进一步简化为
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake UoF returns fake Data from DB
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork
.Setup(x => x.SomeRepository.GetAsync()) //<-- note the full call here
.ReturnsAsync(fakeData); //<-- and also the use of the ReturnsAsync here
错误的对象也基于控制器。传递 mock 的对象。
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory.Object);
然后应等待正在测试的方法的执行。
//Our async method
var result = await controller.SomeMethod() as PartialViewResult;
并且可以对结果进行断言以验证行为。
从本质上讲,问题出在测试的安排和执行方式上。不是正在测试的代码。
这是重构后的测试
public async Task GetExchange_GetView_OkModelIsViewModel() {
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake UoF returns fake Data from DB
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork
.Setup(x => x.SomeRepository.GetAsync())
.ReturnsAsync(fakeData);
//fake factory create fake UoF
var fakeUnitOfWorkFactory = new Mock<UnitOfWorkFactory>();
fakeUnitOfWorkFactory.Setup(x => x.Create()).Returns(mockUnitOfWork.Object);
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory.Object);
//Act
//Our async method
var result = await controller.SomeMethod() as PartialViewResult;
//---Assert--
result.Should().NotBeNull();
result.Model.Should().NotBeNull();
CollectionAssert.AreEquivalent(fakeData, result.Model as ICollection);
}
我有一个 ASP.NET MVC 应用程序,其控制器具有异步方法,返回 Task<PartialViewResult>
对象并标有 async 关键字。
该方法仅以异步方式从数据库中获取数据。
public async Task<PartialViewResult> SomeMethod()
{
using (var unitOfWork = _factory.Create())
{
var result = await unitOfWork.SomeRepository.GetAsync();
return PartialView(result);
};
}
在测试期间,流只是冻结在这个位置(在 运行 时此代码运行良好):
var models = await unitOfWork.SomeRepository.GetAsync();
这是我对这个方法的测试:
public void GetExchange_GetView_OkModelIsViewModel()
{
//fake Repository returns fake Data from DB
var mockSomeRepository = new Mock<ISomeRepository>();
mockSomeRepository.Setup(x => x.GetAsync(...).Returns(new Task<List<SomeType>>(() => new List<SomeType>()));
//fake UoW returns fake Repository
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.SomeRepository).Returns(mockSomeRepository.Object);
//fake factory create fake UoW
var fakeUnitOfWorkFactory = new Mock<UnitOfWorkFactory>();
fakeUnitOfWorkFactory.Setup(x => x.Create()).Returns(mockUnitOfWork.Object);
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory);
//Our async method
var result = controller.SomeMethod();
result.Wait();
//---Assert--
}
问题:为什么我的方法中的流在测试执行期间冻结???
更新
如果我替换
var result = await unitOfWork.SomeRepository.GetAsync();
到
var models = unitOfWork.SomeRepository.GetAsync();
models.Start();
models.Wait();
var result = models.Result;
但我不太明白为什么会这样。有人可以解释一下吗?
测试异步方法时,您的测试方法也应该是异步的。 NUnit 可以毫无问题地处理这个问题。
[Test]
public async Task GetExchange_GetView_OkModelIsViewModel() {
// ...
var controller = new SomeController(fakeUnitOfWorkFactory);
var result = await controller.SomeMethod(); // call using await
// ...
}
why is the stream in my method freezes during test execution?
测试有一些问题。
最初的示例是将阻塞调用 (.Wait()
) 与导致死锁的异步调用混合在一起,从而导致挂起(死锁)。
测试应该一直转换为异步。测试运行器应该能够毫无问题地处理它。
public async Task GetExchange_GetView_OkModelIsViewModel() { ... }
接下来 GetAsync
方法的设置没有正确完成。
因为该方法未配置为 return 允许代码继续执行的已完成任务,这也会导致该任务阻塞
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake Repository returns fake Data from DB
var mockSomeRepository = new Mock<ISomeRepository>();
mockSomeRepository
.Setup(x => x.GetAsync())
.Returns(Task.FromResult(fakeData)); // <-- note the correction here
根据测试需要,设置可以进一步简化为
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake UoF returns fake Data from DB
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork
.Setup(x => x.SomeRepository.GetAsync()) //<-- note the full call here
.ReturnsAsync(fakeData); //<-- and also the use of the ReturnsAsync here
错误的对象也基于控制器。传递 mock 的对象。
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory.Object);
然后应等待正在测试的方法的执行。
//Our async method
var result = await controller.SomeMethod() as PartialViewResult;
并且可以对结果进行断言以验证行为。
从本质上讲,问题出在测试的安排和执行方式上。不是正在测试的代码。
这是重构后的测试
public async Task GetExchange_GetView_OkModelIsViewModel() {
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake UoF returns fake Data from DB
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork
.Setup(x => x.SomeRepository.GetAsync())
.ReturnsAsync(fakeData);
//fake factory create fake UoF
var fakeUnitOfWorkFactory = new Mock<UnitOfWorkFactory>();
fakeUnitOfWorkFactory.Setup(x => x.Create()).Returns(mockUnitOfWork.Object);
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory.Object);
//Act
//Our async method
var result = await controller.SomeMethod() as PartialViewResult;
//---Assert--
result.Should().NotBeNull();
result.Model.Should().NotBeNull();
CollectionAssert.AreEquivalent(fakeData, result.Model as ICollection);
}