模拟使用 IHttpClientFactory.CreateClient 创建的 HttpClient

Mocking an HttpClient created using IHttpClientFactory.CreateClient

如标题所示,我有一些代码调用 IHttpClientFactory.CreateClient() 来创建 HttpClient 实例。

我在 .Net Core 3.1 中这样做

我需要嘲笑这个。根据 以下应该有效...

[Test]
public void Mytest() {

    var httpClientFactory = new Mock<IHttpClientFactory>(MockBehavior.Strict);

    httpMessageHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
    httpMessageHandler.Protected()
        // Setup the PROTECTED method to mock
        .Setup<Task<HttpResponseMessage>>(
            "SendAsync",
            ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()
        )
        // prepare the expected response of the mocked http call
        .ReturnsAsync(new HttpResponseMessage()
        {
            StatusCode = HttpStatusCode.BadRequest,
        })
        .Verifiable();

    var httpClient = new HttpClient(httpMessageHandler.Object);

    httpClientFactory.Setup(_ => _.CreateClient())   // This fails
        .Returns(httpClient).Verifiable();

    systemUnderTest = new MyService(httpClientFactory.Object);
    var result = systemUnderTest.MyMethod()

    // Assert Stuff
}

但是,当我运行它的时候,报如下...

System.NotSupportedException : Unsupported expression: _ => _.CreateClient() Extension methods (here: HttpClientFactoryExtensions.CreateClient) may not be used in setup / verification expressions.

我显然做错了什么,但我看不出是什么。

谁能指点一下?

似乎是因为错误报告“扩展方法不可验证”,在这种情况下,CreateClient 是一个扩展方法,不能对其附加 Verifiable() 调用。

您希望在这里测试什么?您真的需要验证是否调用了 CreateClient(可能不需要)吗?事实上,您是否需要验证是否调用了这些方法中的任何一个?

对我来说,应该编写此测试来检查方法在获得 'Bad request' returned 时的输出。没有必要 'verify' 方法内的调用,因为您开始真正对方法的内部行为进行单元测试,而不是测试该方法的预期输出。

TLDR; 删除验证调用并更改测试以检查预期的 return 行为,而不是添加难以维护的内部实现检查。

IHttpClientFactory has a single method on it, Create(string). It also has an extension method Create(IHttpClientFactory) that uses the default configuration (it passes Options.DefaultName).

您不是在模拟接口方法,而是在模拟扩展方法,正如您已经意识到的那样,模拟扩展方法是不可能的。不过别怕,我们有解决办法:模拟界面上实际出现的方法!

您可以模拟所有客户端名称、特定名称或默认 名称 (string.Empty):

// any name
httpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())) 
        .Returns(httpClient).Verifiable();

// specific name
httpClientFactory.Setup(_ => _.CreateClient("SpecificName")) 
        .Returns(httpClient).Verifiable();

// the default name (extension method invokes this)
httpClientFactory.Setup(_ => _.CreateClient(string.Empty)) 
        .Returns(httpClient).Verifiable();

最后一个选项与调用扩展方法时发生的情况相匹配。但请记住,如果您使用的是 命名客户端 ,您的代码可能会向工厂传递一个名称,而您希望匹配该名称。