我有模拟 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();
}
}
答案 0 :(得分:1)
这两种测试设置的不同之处在于在测试执行期间创建了多少个 StreamContent
类的实例。
HttpResponseMessage
类的多个实例,并引用作为参数传递给 StreamContent
方法的同一个 MockClient
实例。.Returns(() => Task.FromResult(new HttpResponseMessage {StatusCode = status,
Content = content}))
HttpResponseMessage
类的多个实例,每个 _client.Get
方法执行一个,但每个 HttpResponseMessage
实例都有自己的 StreamContent
实例。.Returns(() => Task.FromResult(new HttpResponseMessage {StatusCode = HttpStatusCode.OK,
Content = new StreamContent(content)}))
在提供的代码中,_client.Get
方法在单个测试执行期间被调用两次。第一次在 GetLatest
方法中,第二次在 GetAllAsync
方法中。每个方法读取响应内容并对其进行处理。因此,在第一次设置的情况下,GetAllAsync
方法在尝试将响应内容作为流读取时接收 ObjectDisposedException
,因为响应内容的单个实例已经在 GetLatest
方法中处理.
所有一次性对象的创建次数应与它们在被测代码中检索和处理的次数相同。
作为一种可能的解决方案,MockClient
方法应更改为接受字节数组而不是 StreamContent,并为每个 HttpResponseMessage
响应创建单独的 StreamContent 和 MemoryStream 实例。
private void MockClient(HttpStatusCode status, byte[] content = null)
{
_client
.Setup(m => m.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(
new HttpResponseMessage
{
StatusCode = status,
Content = content == null
? null
: new StreamContent(new MemoryStream(content))
}))
.Verifiable();
}
[Fact]
public async Task Get_SerializerFailure_Throws()
{
MockClient(HttpStatusCode.OK, new byte[5]);
_jsonSerializer.Setup(x => x.DeserializeFromStream<Dto>(It.IsAny<Stream>())).Throws<JsonReaderException>();
await Assert.ThrowsAsync<JsonReaderException>(() => _sut.GetAllAsync());
}
另一种选择是将 Func<StreamContent>
委托传递给 MockClient
方法。
private void MockClient(HttpStatusCode status, Func<StreamContent> content = null)
{
_client
.Setup(m => m.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(
new HttpResponseMessage
{
StatusCode = status,
Content = content == null
? null
: content()
}))
.Verifiable();
}
[Fact]
public async Task Get_SerializerFailure_Throws()
{
MockClient(HttpStatusCode.OK, () => new StreamContent(new MemoryStream(new byte[5])));
_jsonSerializer.Setup(x => x.DeserializeFromStream<Dto>(It.IsAny<Stream>())).Throws<JsonReaderException>();
await Assert.ThrowsAsync<JsonReaderException>(() => _sut.GetAllAsync());
}