【发布时间】:2021-08-01 18:09:16
【问题描述】:
我有模拟 http 客户端的功能:
private void MockClient(HttpStatusCode status, HttpContent content = null)
{
_client
.Setup(m => m.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(new HttpResponseMessage {StatusCode = status, Content = content})).Verifiable();
}
我使用它进行这样的测试:
[Fact]
public async void Get_SerializerFailure_Throws()
{
var content = new MemoryStream(new byte[5]);
MockClient(HttpStatusCode.OK, new StreamContent(content));
_jsonSerializer.Setup(x => x.DeserializeFromStream<Dto>(It.IsAny<Stream>())).Throws<JsonReaderException>();
await Assert.ThrowsAsync<JsonReaderException>(() => _sut.GetAllAsync());
}
这里出现错误:
typeof(System.ObjectDisposedException): Cannot access a disposed object.
Object name: 'System.Net.Http.StreamContent'.
但是,如果我像这样直接在测试中模拟客户端,它可以工作并且不会抛出此错误:
[Fact]
public async void Get_ProtobufSerializerFailure_Throws()
{
var content = new MemoryStream(new byte[5]);
_client
.Setup(m => m.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(new HttpResponseMessage {StatusCode = HttpStatusCode.OK, Content = new StreamContent(content)})).Verifiable();
_protobufSerializer.Setup(x => x.DeserializeFromStream<Dto>(It.IsAny<Stream>())).Throws<JsonReaderException>();
await Assert.ThrowsAsync<JsonReaderException>(() => _sut.GetAllAsync());
}
我不明白这里有什么区别以及为什么一种方法有效而另一种方法无效以及如何修复我的模拟方法。
我正在测试这些方法:
private async Task<Object> GetLatest()
{
using (var response = await _client.Get($"{_serviceUrl}", CancellationToken.None))
using (var stream = await response.Content.ReadAsStreamAsync())
{
return _jsonSerializer.DeserializeFromStream<Objects>(stream)?.Items.Last();
}
}
public async Task<IReadOnlyList<(ulong key, Dto item)>> GetAllAsync()
{
var latest = await GetLatest();
using (var response = await _client.Get(url,CancellationToken.None))
using (var stream = await response.Content.ReadAsStreamAsync())
using (var decompressionStream = new GZipStream(stream, CompressionMode.Decompress))
{
var result = _protobufSerializer.DeserializeFromStream<Dto>(decompressionStream);
return result.ToList();
}
}
【问题讨论】:
-
首先避免使用
async void,使用async Task。其次,展示正在测试的主题,以便我们更好地了解您要测试的内容 -
更新问题@Nkosi
-
问题出在范围上。在第一个中,流内容作为变量传递给返回函数,并将在
Get第一次被GetLatest调用并且响应被处理时被处理。在第二个示例中,每次调用返回函数时都会创建一个新的内容流。 -
但是在我运行程序的时候效果很好,但是在单元测试中就不行了
-
因为在程序中实际客户端返回不同的响应。在您的单元测试中,您的模拟每次都返回相同的客户端响应内容。
标签: c# asp.net-core .net-core dotnet-httpclient dispose