使用 IHttpClientFactory.CreateClient 创建的 HttpClient 如何进行模拟测试。

7
如标题所示,我有一些代码调用IHttpClientFactory.CreateClient()来创建一个HttpClient实例。
我正在使用.Net Core 3.1。
我需要进行模拟。根据这个问题 "C# Mock IHttpclient & CreateClient",以下内容应该有效...
[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: 不支持的表达式:_ => _.CreateClient()。不可在设置/验证表达式中使用扩展方法(此处为HttpClientFactoryExtensions.CreateClient)。

很明显我做错了什么,但我看不出来是什么。

有人能提供一些指针吗?


1
这不是一个直接的答案,但是 Flurl 库使得使用 HttpClient 进行单元测试非常舒适。它是一个非常轻量级的 HttpClient 包装器,具有最少的依赖关系。 - martinstoeckli
2个回答

18

IHttpClientFactory 有一个方法:Create(string)。它还有一个扩展方法 Create(IHttpClientFactory),该方法使用默认配置(它传递了 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();

最后一个选项与调用扩展方法时发生的情况相匹配。但请记住,如果您正在使用命名客户端,则您的代码可能会将名称传递给工厂,因此您需要匹配该名称。


这旨在回答您的具体问题并解决所述问题。话虽如此,我同意@Paddy的观点,即您可能正在尝试验证错误的行为。 - pinkfloydx33

0
在这种情况下,似乎非常类似于错误报告“扩展方法不可验证”,在这种情况下,CreateClient是一种扩展方法,不能在其上添加Verifiable()调用。
你希望在这里测试什么?你真的需要验证是否调用了CreateClient(可能不需要)吗?实际上,你需要验证任何这些方法是否被调用吗?
对我来说,这个测试应该被编写为检查当它返回“Bad request”时方法的输出。没有必要“验证”方法内部的调用,因为你开始真正地单元测试方法的内部行为,而不是测试该方法的预期输出。
TLDR; 删除验证调用并更改测试以检查预期的返回行为,而不是添加难以维护的内部实现检查。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接