今天,在使用异步ApiControllers创建Web API时遇到了问题。 我正在使用MongoDB,因为C#驱动程序不支持异步,我试图在我的存储库层实现它。
Building Building中生成的方法如下所示:
public async Task<IEnumerable<Building>> GetAll()
{
var tcs = new TaskCompletetionSource<IEnumerable<Building>>();
await Task.Run(() => {
var c = this.MongoDbCollection.FindAll();
tcs.SetResult(c);
});
return await tcs.Task;
}
现在,当使用NUnit自行测试存储库时,这非常有效。
但是当从控制器进行测试时(使用HttpClient),它在运行tcs.SetResult(c)
后永远不会进入“返回”行。测试一直持续运行,直到我手动中止。
当我删除Task.Run代码并同步执行所有操作时,一切正常:
public async Task<IEnumerable<Building>> GetAll()
{
var c = this.MongoDbCollection.FindAll();
return c;
}
有没有人知道为什么我在测试存储库+数据库时以及测试 controller + repository + database 时会遇到不同的行为?
控制器方法如下所示:
(使用Ninject在构造函数中注入buildingRepository
)
public async Task<HttpResponseMessage> Get()
{
var result = await this.buildingRepository.GetAll();
return Request.CreateResponse(HttpStatusCode.OK, result);
}
编辑:这也是测试方法。第一个是不工作的:
(this.client
是一个HttpClient对象,其accept-header设置为"application/json"
)
[Test]
public void Get_WhenBuildingsExist_ShouldReturnBuilding()
{
var task = this.client.GetAsync("/api/building/");
var result = task.Result.Content.ReadAsStringAsync().Result;
var o = JsonConvert.DeserializeObject<IEnumerable<Building>>(result);
Assert.That(o.Any());
}
[Test]
public void Get_WhenBuildingsExist_ShouldReturnAtLeastOneBuilding()
{
var buildings = this.buildingRepository.GetAll().Result;
Assert.That(buildings.Any());
}
答案 0 :(得分:3)
我读到的帖子解释了为什么从异步任务调用.Results
是一个坏主意,但我目前还没有。基本上,你通过这样做来杀死异步处理。尝试更改您的测试,如下所示:
[Test]
public void Get_WhenBuildingsExist_ShouldReturnBuilding()
{
var task = this.client.GetAsync("/api/building/");
var resultTask = task.Result.Content.ReadAsStringAsync();
resultTask.Wait();
var result = resultTask.Result;
var o = JsonConvert.DeserializeObject<IEnumerable<Building>>(result);
Assert.That(o.Any());
}
答案 1 :(得分:2)
最佳选择是升级到支持异步单元测试的NUnit版本,并将所有Result
/ Wait
次调用更改为await
:
[Test]
public async Task Get_WhenBuildingsExist_ShouldReturnBuilding()
{
var content = await this.client.GetAsync("/api/building/");
var result = await content.ReadAsStringAsync();
var o = JsonConvert.DeserializeObject<IEnumerable<Building>>(result);
Assert.That(o.Any());
}
如果这不可能(例如,在撰写本文时,Xamarin仍然运行非常旧版本的NUnit),那么您可以使用AsyncContext
from my AsyncEx library:
[Test]
public void Get_WhenBuildingsExist_ShouldReturnBuilding()
{
var o = AsyncContext.Run(async () =>
{
var content = await this.client.GetAsync("/api/building/");
var result = await content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<Building>>(result);
});
Assert.That(o.Any());
}