【问题标题】:readonly object for unit testing is being disposed when using 'using'使用“使用”时正在处理单元测试的只读对象
【发布时间】:2020-03-09 20:40:24
【问题描述】:

我有一个对象在同一个单元测试中使用了很多次,在同一个类的其他单元测试中,它的定义如下:

private readonly Task<HttpResponseMessage> successfulResponse = Task.FromResult(
            new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.OK,
                Content = new StringContent(@"{""EntitySets"":[{""ResultSets"":[{""Results"":[{""Type"":""Message""}],""Total"":2}],""EntityType"":""Message""}]}")
            });

然后在我的每个相关单元测试中,我都在做这样的事情:

mockHttpClient.Setup(m => m.SendAsync(It.Is<HttpRequestMessage>(v =>
            v.RequestUri.Host.Equals("example.com")),
            It.IsAny<CancellationToken>())).Returns(successfulResponse);

...其中mockHttpClient 是模拟的HttpClient

但是当我进行单元测试时,这种逻辑会在被测试代码中的很多地方执行:

using (HttpResponseMessage response = await this.client.SendAsync(request, cancellationToken))
{
//...
}

... 其中this.client 是上面的mockHttpClient。如您所知,Moq 在这里提供了我预先准备好的 successfulResponse 对象来模拟 http 响应。

问题是,当第一次执行这样的using 语句时,当它离开using 块时,它会释放successfulResponse,以及所有后续using块失败并出现以下异常:

Test method TestProbe_Success threw exception: 
    System.ObjectDisposedException: Cannot access a disposed object.
    Object name: 'System.Net.Http.StringContent'.

所以我的问题是,我怎样才能防止被测试的代码处理这个在同一个单元测试和其他单元测试中多次使用的对象?

【问题讨论】:

  • 你想要改变被测试方法的行为,因为测试?那没有意义。让它继续处理它,每次只提供一个新的 HttpResponseMessage 实例。
  • 如果您使用工厂模式在代码中检索客户端,那么您可以在单元测试中模拟工厂以返回模拟客户端(取决于您的架构)。然后在您的模拟客户端中,您可以覆盖 dispose 以不执行任何操作。
  • 我想使用using 方法,因为实际上当收到实际响应时,每次都会是不同的http响应实例。这个问题仅在我使用相同的响应对象
  • 您的问题不清楚。这种行为的原因是显而易见的。你想做的不是。如果你想继续重用同一个对象,你为什么要处理它(即为什么使用using)?或者,如果您希望一个对象不受处置,为什么不实现自己的响应类版本以用于测试目的,它忽略对Dispose() 的调用?您希望我们在您的代码中对这个问题做些什么?

标签: c# unit-testing moq dispose using


【解决方案1】:

问题是您在测试中使用了共享资源,如果由一个测试处理,则会在另一个测试中造成不必要的影响。

改成函数

private readonly Func<Task<HttpResponseMessage>> successfulResponse = () => Task.FromResult(
        new HttpResponseMessage() {
            StatusCode = System.Net.HttpStatusCode.OK,
            Content = new StringContent(@"{""EntitySets"":[{""ResultSets"":[{""Results"":[{""Type"":""Message""}],""Total"":2}],""EntityType"":""Message""}]}")
        });

并让设置在其返回中使用委托

mockHttpClient
    .Setup(m => m.SendAsync(It.Is<HttpRequestMessage>(v => 
            v.RequestUri.Host.Equals("example.com")
        ),
        It.IsAny<CancellationToken>())
    )
    .Returns(() => successfulResponse());

这样每次返回响应时,都会使用一个新实例。

【讨论】:

    猜你喜欢
    • 2010-11-06
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多