【问题标题】:How to verify request body content in Flurl Http in unit tests?如何在单元测试中验证 Flurl Http 中的请求正文内容?
【发布时间】:2019-11-15 04:20:22
【问题描述】:

我正在使用 Flurl Http 发出 http 请求。在单元测试中,我试图验证预期的内容是否已传递给发件人。我正在尝试:

httpTest.ShouldHaveCalled(url)
        .WithVerb(HttpMethod.Post)
        .WithContentType(contentType)
        .With(w => w.Request.Content.ReadAsStringAsync().Result == content)
        .Times(1);

但是,System.ObjectDisposedException Cannot access a disposed object. Object name: 'System.Net.Http.StringContent'. 失败

看起来 Flurl 在测试中完成验证之前正在处理请求正文内容。如何捕获请求正文以进行验证?

编辑(一个完全可重现的例子):

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

using Autofac.Extras.Moq;

using Flurl.Http;
using Flurl.Http.Testing;

using Xunit;

namespace XUnitTestProject1
{
    public class MyClassTest : IDisposable
    {
        private readonly AutoMock container;
        private readonly HttpTest client;

        public MyClassTest()
        {
            this.container = AutoMock.GetLoose();
            this.client = new HttpTest();
        }

        [Fact]
        public async Task SendAsync_ValidateRequestBody()
        {
            const string url = "http://www.example.com";
            const string content = "Hello, world";

            var sut = this.container.Create<MyClass>();
            await sut.SendAsync(url, content);

            this.client.ShouldHaveCalled(url)
                .With(w => w.Request.Content.ReadAsStringAsync().Result == content);
        }

        public void Dispose()
        {
            this.container?.Dispose();
            this.client?.Dispose();
        }
    }

    public class MyClass
    {
        public virtual async Task SendAsync(string url, string content)
        {
            await url.PostAsync(new StringContent(content, Encoding.UTF8, "text/plain"));
        }
    }
}

【问题讨论】:

    标签: c# unit-testing flurl


    【解决方案1】:

    在大多数情况下(参见下面的编辑),Flurl 已经捕获了它,您只需以不同的方式访问它。

    在您的示例中,w.Request 是来自HttpClient 堆栈的“原始”HttpRequestMessage,Flurl 公开了该堆栈,因此您可以在需要时深入了解。 HttpRequestMessage.Content 是一个只读的、只进的流,在您访问它时已经被读取和释放。

    要断言捕获的字符串主体,您通常只需这样做:

    httpTest.ShouldHaveCalled(url)
        ...
        .WithRequestBody(content)
    

    编辑

    正如您所指出的,这不适用于您使用 Flurl 的方式。 StringContent 包含的字符串实际上是只写的,即没有属性将其公开以供读取。这就是 Flurl 的CapturedStringContent 的目的。如果您使用该类型作为StringContent 的直接替代品,则RequestBody 将在您的测试中可用。

    文档中没有很好地介绍这一点的原因是,如果您以“Flurl 方式”做事,那么您一开始就没有明确地创建内容对象。 PostStringAsync and PostJsonAsync 是发送 POST 请求的更为常见的方式,两者都是使用 CapturedStringContent 实现的。如果可以,请使用其中一种方法,或者如果您出于某种原因需要获取较低级别的内容对象,请使用 PostAsync(new CapturedStringContent(...))

    【讨论】:

    • 这实际上是我最初尝试的,但x.RequestBody 始终为空。我想这与Available ONLY if Request.Content is a Flurl.Http.Content.CapturedStringContent 的代码文档有关。不确定这实际上意味着什么,但上述建议不起作用。
    • 已编辑,我忘记了断言请求正文(如果可能)比我最初发布的更容易:)
    • 抱歉,.WithRequestBody(content) 也不起作用。测试在不应该失败的时候失败了。您可以尝试我粘贴的代码,它是独立的,应该能够重现问题。
    • 我认为您没有阅读我的整个编辑。避免创建StringContent 并使用我建议的替代方法之一。否则将无法正常工作。
    • 抱歉,我的错误。它与CapturedStringContent 合作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-14
    • 2015-04-12
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    相关资源
    最近更新 更多