模拟使用IHttpClientFactory.CreateClient创建的HttpClient

时间:2020-06-22 10:35:10

标签: c# unit-testing httpclientfactory

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

我正在.Net Core 3.1中做到这一点

我需要嘲笑这个。根据{{​​3}},以下方法应该有效...

[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)可能无法在安装程序中使用/ 验证表达式。

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

任何人都可以提供任何指针吗?

2 个答案:

答案 0 :(得分:4)

IHttpClientFactory上有一个单一方法Create(string)。它还具有使用默认配置(Create(IHttpClientFactory))的扩展方法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();

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

答案 1 :(得分:1)

错误报告“扩展方法不可验证”的情况似乎非常多,在这种情况下,CreateClient是扩展方法,不能在其上加上Verifiable()调用。

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

对我来说,应该编写此测试以检查该方法返回“错误请求”时的输出。无需“验证”方法中的调用,因为您开始真正地对方法的内部行为进行单元测试,而不是测试该方法的预期输出。

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