【问题标题】:Task.Wait always returns false although task finishedTask.Wait 总是返回 false 虽然任务完成
【发布时间】:2017-09-25 00:22:51
【问题描述】:

我正在使用HttpClient 尝试在 Web API 控制器中执行 POST 方法。控制器方法是同步的。我是这样做的:

var response = owin.HttpClient.PostAsJsonAsync(uri, body);

之后我打电话给Wait

var result = response.Wait(15000);

运行此代码时,我看到 http 完成执行,但 result 值始终为 false。我会错过什么?

编辑: 我现在尝试了一种异步方法,但它也没有帮助我

public IHttpActionResult Add(Item item)
{
    var result = _db.AddItem(item);
    return Ok(result);
}

测试项目:

TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
    return await 
   _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}

public async Task<ItemPreview> Method2(object body);
{
     return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}

[TestMethod]
public void test1()
{
    Item item = new(...);
    Method2(item).Continue with(task => {// Never reach     here }
}

我做错了什么? 调试时,我看到控制器方法返回了一个很好的响应,但它永远不会回到我的测试中

【问题讨论】:

  • 如果您使用异步 API,调用 Wait() 有什么意义?你最好使用await
  • 因为没有办法使用HttpClient以同步方式发布数据,否则我会同步进行。由于某种原因,当我尝试使用 await 时遇到了死锁
  • 首先,我不会在async 中推荐Wait,始终使用await。其次,请发布导致死锁的完整代码,这样我们就可以对实际问题进行排序,而不是混合阻塞和非阻塞代码。
  • @Razort4x 我发布了我的代码

标签: c# unit-testing asp.net-web-api async-await owin


【解决方案1】:

您正在混合异步调用和阻塞调用(即.Result.Wait()),这会导致死锁。

这看起来更像是测试客户端的阻塞问题。

如果你想等待来自服务器的结果,你需要在这种情况下让测试一直异步。

将测试方法转换为异步

[TestMethod]
public async Task test1() {
   //Arrange
   Item item = new Item(...);

   //Act
   var preview = await Method2(item);

   //Assert
   Assert.IsNotNull(preview);      
}

并更新方法以不混合异步和阻塞调用。

Method1 不需要asyn/await,如果它在调用后没有使用任务,因此可以将其删除,只需让方法返回可以等待的Task

TestServer _owinTestServer;
public Task<HttpResponse> Method1(string url, object body) {
    return _owinTestServer.HttpClient.PostAsJsonAsync(url, body);
}

Method2 需要await 来自Method1 的响应,然后获取其内容。

public async Task<ItemPreview> Method2(object body) {
    var response = await Method1("..", body );
    return await response.Content.ReadAsAsync<ItemPreview>();
}

【讨论】:

    【解决方案2】:

    控制器方法是否同步并不重要。这纯粹是对服务器代码的关注。由于PostAsJsonAsync方法是异步的,所以需要await它:

    var response = await owin.HttpClient.PostAsJsonAsync(uri, body);
    

    这将允许您的代码等待来自服务器的响应。

    【讨论】:

      【解决方案3】:

      我假设您的初始代码看起来像这样......

      var response = owin.HttpClient.PostAsJsonAsync(uri, body);
      var result = response.Wait(15000);
      var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
      ...
      

      是的,等待 15 秒后结果将是错误的。这是因为您已经设置了请求,即 A.K.A 响应,但您实际上并没有进行调用,并且任何响应。Wait(n) 将返回 false。

      您只需要启动 ReadAsAsync...

      var response = owin.HttpClient.PostAsJsonAsync(uri, body);
      var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
      

      但是,我想你会发现你可以一起跳过 response.Wait(n),因为它会返回 true,因为 ReadAsAsync() 将等待返回或失败。如果您希望配置请求超时,您可以在 HttpClient 中执行此操作,并且您的 ReadAsAsync 将抛出一个带有 TaskCanceledException 的 InnerException 的 AggregateException。

      附带说明,您不需要使用 owin.HttpClient,您只需实例化一个新的 HttpClient。我相信您所指的 owin 对象是用于自托管您的 WebApi,我不知道这是否重要。但是假设您在 WebApi 上调用 Add(Item item) 并且 db.AddItem(item) 将返回 ItemPreview 对象,您的代码可能如下所示:

      [TestMethod]
      public void test1()
      {
          Item item = new(...);
          var uri = "..";
          var client = new HttpClient();
          var response = client.PostAsJsonAsync(uri, item);
          var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
      
          /*  The things to happen once you have item preview */
      
      }
      

      【讨论】:

        【解决方案4】:

        结果可能始终为 false,因为 _db.AddItem 始终返回 false。如果没有,我已经对您的代码进行了理想的更改,这应该适合您

        TestServer _owinTestServer;
        public async Task<HttpResponse message> Method1(string url, object body)
        {
            return await _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
        }
        
        public async Task<ItemPreview> Method2(object body);
        {
             return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
        }
        
        [TestMethod]
        public void test1()
        {
            Item item = new(...);
            await Method2(item).ContinueWith(task => {// Never reach     here }
        }
        

        由于 Method2 返回一个异步任务,ContinueWith 不会等待它完成,因此它可能需要 await 方法调用。

        【讨论】:

          猜你喜欢
          • 2023-03-10
          • 2017-07-20
          • 1970-01-01
          • 1970-01-01
          • 2015-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多