![](/img/trans.png)
[英]Unit Testing Async Controller Method Returning Null with NUnit and Moq
[英]Testing an async method in ASP MVC Controller with Nunit
我有一个ASP.NET MVC应用程序,其中一个Controller具有异步方法,返回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--
}
问题 :为什么我的方法中的流在测试执行期间冻结?
UPDATE
如果我更换,该测试开始起作用
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
// ...
}
为什么我的方法中的流在测试执行期间冻结?
测试有一些问题。
最初的例子是将阻塞调用( .Wait()
)与异步调用混合在一起导致死锁,从而导致挂起(死锁)。
测试应该一直转换为异步。 测试运行器应该能够毫无问题地处理它。
public async Task GetExchange_GetView_OkModelIsViewModel() { ... }
接下来, GetAsync
方法的设置未正确完成。
由于该方法未配置为返回允许代码继续的已完成任务,因此也会导致该任务的阻塞
//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
错误的对象也是基于控制器。 传递模拟的对象。
//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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.