【问题标题】:Mocking HttpResponse WriteAsync模拟 HttpResponse WriteAsync
【发布时间】:2018-04-27 15:02:15
【问题描述】:

我正在尝试调用在模拟HttpResponse 上模拟的 WriteAsync,但我不知道要使用的语法。

var responseMock = new Mock<HttpResponse>();
responseMock.Setup(x => x.WriteAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()));

ctx.Setup(x => x.Response).Returns(responseMock.Object);

测试炸弹出现以下错误:

System.NotSupportedException:扩展方法上的设置无效:x => x.WriteAsync(It.IsAny(), It.IsAny())

最终我想验证是否已将正确的字符串写入响应。

如何正确设置?

【问题讨论】:

    标签: c# unit-testing asp.net-core moq xunit


    【解决方案1】:

    为了完整起见,这是一个似乎适用于 .NET Core 3.1 的解决方案:

    const string expectedResponseText = "I see your schwartz is as big as mine!";
    
    DefaultHttpContext httpContext = new DefaultHttpContext();
    httpContext.Response.Body = new MemoryStream();
    
    // Whatever your test needs to do
    
    httpContext.Response.Body.Position = 0;
    using (StreamReader streamReader = new StreamReader(httpContext.Response.Body))
    {
        string actualResponseText = await streamReader.ReadToEndAsync();
        Assert.Equal(expectedResponseText, actualResponseText);
    }
    

    【讨论】:

      【解决方案2】:

      起订量不能Setup 扩展方法。如果您知道扩展方法访问的内容,那么在某些情况下,您可以通过扩展方法模拟一条安全路径。

      WriteAsync(HttpResponse, String, CancellationToken)

      将给定的文本写入响应正文。将使用 UTF-8 编码。

      通过以下重载直接访问HttpResponse.Body.WriteAsync,其中BodyStream

      /// <summary>
      /// Writes the given text to the response body using the given encoding.
      /// </summary>
      /// <param name="response">The <see cref="HttpResponse"/>.</param>
      /// <param name="text">The text to write to the response.</param>
      /// <param name="encoding">The encoding to use.</param>
      /// <param name="cancellationToken">Notifies when request operations should be cancelled.</param>
      /// <returns>A task that represents the completion of the write operation.</returns>
      public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
      {
          if (response == null)
          {
              throw new ArgumentNullException(nameof(response));
          }
      
          if (text == null)
          {
              throw new ArgumentNullException(nameof(text));
          }
      
          if (encoding == null)
          {
              throw new ArgumentNullException(nameof(encoding));
          }
      
          byte[] data = encoding.GetBytes(text);
          return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);
      }
      

      这意味着你需要模拟 response.Body.WriteAsync

      //Arrange
      var expected = "Hello World";
      string actual = null;
      var responseMock = new Mock<HttpResponse>();
      responseMock
          .Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(),It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
          .Callback((byte[] data, int offset, int length, CancellationToken token)=> {
              if(length > 0)
                  actual = Encoding.UTF8.GetString(data);
          })
          .ReturnsAsync();
      
      //...code removed for brevity
      
      //...
      Assert.AreEqual(expected, actual);
      

      回调用于捕获传递给模拟成员的参数。它的值存储在一个变量中,以便稍后在测试中断言。

      【讨论】:

      • 这是一个很好的答案,但不幸的是不适用于 .NET Core 3.1 - 扩展方法变得更加复杂。我将不胜感激任何指导...谢谢。
      • @StevenDarby 我得去看看新版本,看看有什么变化。您是否有指向新来源的链接,以免我追查它?
      • @StevenDarby Ok 找到了新来源。检查出来。
      • @StevenDarby 是的,在最近的版本中事情变得更加复杂。尝试改用 DefaultHttpContext 并修改其响应以适合您的测试用例。
      • 在 .NET Core 3.1 中使用 DefaultHttpContext 也对我有用。唯一的事情是使用 MemoryStream (stackoverflow.com/questions/45959605/…) 设置 Response.Body,因为我在单元测试中断言了正文内容
      猜你喜欢
      • 1970-01-01
      • 2020-08-03
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多