我有一个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--
}
问题:为什么我的方法中的流在测试执行期间会冻结???
更新
如果我替换了
,这个测试就开始起作用了var result = await unitOfWork.SomeRepository.GetAsync();
到
var models = unitOfWork.SomeRepository.GetAsync();
models.Start();
models.Wait();
var result = models.Result;
但我不太明白为什么会这样。有人可以解释一下吗?
答案 0 :(得分:3)
在测试异步方法时,您的测试方法也应该是异步的。 NUnit可以毫无问题地处理这个问题。
[Test]
public async Task GetExchange_GetView_OkModelIsViewModel() {
// ...
var controller = new SomeController(fakeUnitOfWorkFactory);
var result = await controller.SomeMethod(); // call using await
// ...
}
答案 1 :(得分:0)
为什么我的方法中的流在测试执行期间会冻结?
测试存在一些问题。
最初的例子是将阻塞调用(.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);
}