如何在单元测试中验证 Flurl Http 中的请求正文内容?
How to verify request body content in Flurl Http in unit tests?
我正在使用 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"));
}
}
}
在大多数情况下(见下面的编辑),Flurl 已捕获它,您只需要以不同的方式访问它。
在您的示例中,w.Request
是来自 HttpClient
堆栈的 "raw" HttpRequestMessage,Flurl 公开了它,因此您可以在需要时深入了解. HttpRequestMessage.Content
是一个只读一次的流,在您访问它时已经被读取和处理。
要断言捕获的字符串主体,您通常只需这样做:
httpTest.ShouldHaveCalled(url)
...
.WithRequestBody(content)
编辑
如您所述,根据您使用 Flurl 的方式,这并不适用。 StringContent
包含的字符串实际上是只写的,即没有 属性 公开它以供读取。这就是 Flurl CapturedStringContent
的目的。如果您使用该类型作为 StringContent
的直接替代,RequestBody
将在您的测试中可用。
文档中没有很好地涵盖这一点的原因是,如果您执行某些操作 "the Flurl way",那么您并没有首先明确地创建内容对象。 PostStringAsync
and PostJsonAsync
是发送 POST 请求的更常见的方式,两者都是使用 CapturedStringContent
实现的。如果可以,请使用其中一种方法,或者如果出于某种原因需要获取较低级别的内容对象,请使用 PostAsync(new CapturedStringContent(...))
。
我正在使用 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"));
}
}
}
在大多数情况下(见下面的编辑),Flurl 已捕获它,您只需要以不同的方式访问它。
在您的示例中,w.Request
是来自 HttpClient
堆栈的 "raw" HttpRequestMessage,Flurl 公开了它,因此您可以在需要时深入了解. HttpRequestMessage.Content
是一个只读一次的流,在您访问它时已经被读取和处理。
要断言捕获的字符串主体,您通常只需这样做:
httpTest.ShouldHaveCalled(url)
...
.WithRequestBody(content)
编辑
如您所述,根据您使用 Flurl 的方式,这并不适用。 StringContent
包含的字符串实际上是只写的,即没有 属性 公开它以供读取。这就是 Flurl CapturedStringContent
的目的。如果您使用该类型作为 StringContent
的直接替代,RequestBody
将在您的测试中可用。
文档中没有很好地涵盖这一点的原因是,如果您执行某些操作 "the Flurl way",那么您并没有首先明确地创建内容对象。 PostStringAsync
and PostJsonAsync
是发送 POST 请求的更常见的方式,两者都是使用 CapturedStringContent
实现的。如果可以,请使用其中一种方法,或者如果出于某种原因需要获取较低级别的内容对象,请使用 PostAsync(new CapturedStringContent(...))
。