【问题标题】:C# Mock IHttpclient & CreateClientC# 模拟 IHttpclient 和 CreateClient
【发布时间】:2023-03-25 18:41:02
【问题描述】:

我有一个要进行 x 单元测试的函数,但似乎我必须模拟 CreateClient 函数?每当我在测试期间对其进行调试时,似乎 var 客户端都等于 null。我正在正确地注入依赖项,我确信这一点。我想知道的是如何模拟 CreateClient。

这是那个函数:

    public async Task CreateMessageHistoryAsync(Message message)
    {
        //This seems to be giving a null value
        var client = this.clientFactory.CreateClient(NamedHttpClients.COUCHDB);

        var formatter = new JsonMediaTypeFormatter();
        formatter.SerializerSettings = new JsonSerializerSettings
        {
            Formatting = Formatting.Indented,
            NullValueHandling = NullValueHandling.Ignore,
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };

        Guid id = Guid.NewGuid();            

        var response = await client.PutAsync(id.ToString(), message, formatter);

        if (!response.IsSuccessStatusCode)
        {
            throw new HttpRequestException(await response.Content.ReadAsStringAsync());
        }
    }

这是单元测试,我在一个单独的类中模拟 IHttpClient,我正在使用该类。

    [Collection("MockStateCollection")]
    public class CreateMessageHistory
    {
        private readonly MockStateFixture mockStateFixture;

        public CreateMessageHistory(MockStateFixture mockStateFixture)
        {
            this.mockStateFixture = mockStateFixture;
        }

        [Fact]
        public async Task Should_NotThrowHttpRequestException_When_AMessageHistoryIsCreated()
        {
            var recipients = MockMessage.GetRecipients("Acc", "Site 1", "Site 2", "Site 3");
            var message = MockMessage.GetMessage(recipients);

            mockStateFixture
                .MockMessageHistoryService
                .Setup(service => service.CreateMessageHistoryAsync(message));

            var messageHistoryService = new MessageHistoryService(
                mockStateFixture.MockIHttpClientFactory.Object);

            mockStateFixture.MockIHttpClientFactory.Object.CreateClient("CouchDB");

            var task = messageHistoryService.CreateMessageHistoryAsync(message);
            var type = task.GetType();
            Assert.True(type.GetGenericArguments()[0].Name == "VoidTaskResult");
            Assert.True(type.BaseType == typeof(Task));
            await task;

            //await Assert.IsType<Task>(messageHistoryService.CreateMessageHistoryAsync(message));
            // await Assert.ThrowsAsync<HttpRequestException>(() => messageHistoryService.CreateMessageHistoryAsync(message));
        }
    }

在我看来,我还需要模拟 CreateClient 类是吗?

【问题讨论】:

  • 您要模拟的单独课程在哪里?我们需要看看你在那堂课上做了什么。请张贴代码。

标签: c# unit-testing mocking moq xunit


【解决方案1】:

您应该为已设置CreateClient 方法的ClientFactory 注入一个模拟对象。

// create the mock client
var httpClient = new Mock<IHttpClient>();

// setup method call for client
httpClient.Setup(x=>x.PutAsync(It.IsAny<string>()
                               , It.IsAny<Message>(),
                               , It.IsAny< JsonMediaTypeFormatter>())
          .Returns(Task.FromResult(new HttpResponseMessage { StatusCode = StatusCode.OK}));

// create the mock client factory mock
var httpClientFactoryMock = new Mock<IHttpClientFactory>();

// setup the method call
httpClientFactoryMock.Setup(x=>x.CreateClient(NamedHttpClients.COUCHDB))
                     .Returns(httpClient);

然后你必须将httpClientFactoryMock.Object 传递给构造函数:

var messageHistoryService = new MessageHistoryService(httpClientFactoryMock.Object);

更新

为了对HttpClient 进行单元测试,因为它没有任何接口,您应该按照here 描述的方式包装它。

具体来说,我们要把http客户端安排如下:

// Mock the handler
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

handlerMock.Protected()
// Setup the PROTECTED method to mock
           .Setup<Task<HttpResponseMessage>>("PutAsync",
                                             ItExpr.IsAny<String>(),
                                             ItExpr.IsAny<Message>()
                                             ItExpr.IsAny<MediaTypeFormatter>())
// prepare the expected response of the mocked http call
           .ReturnsAsync(new HttpResponseMessage()
           {
               StatusCode = HttpStatusCode.OK
           })
           .Verifiable();

// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
    BaseAddress = new Uri("http://test.com/"),
};

现在我们应该在调用CreateClient 时返回上面的httpClient

// create the mock client factory mock
var httpClientFactoryMock = new Mock<IHttpClientFactory>();

// setup the method call
httpClientFactoryMock.Setup(x=>x.CreateClient(NamedHttpClients.COUCHDB))
                     .Returns(httpClient);

【讨论】:

  • 没有IHttpClient,只有HttpClient,是不是打错字了?我将其更改为 HttpClient 但 //setup 方法调用的位置部分给出了无法将“Moq.Mock 转换为 'System.Net.HttpClient” 的错误
  • @IvanApungan 我误以为您的客户端实现了一个接口,它是实际的HttpClient。请检查我的更新。那里的链接将指导您如何模拟 http 客户端。所以你应该只从上面改变这个并使 httpClientFactory 返回这个。如果有不清楚的地方,请告诉我。
  • 你先生是个传奇!非常感谢!
  • @IvanApungan 非常欢迎您!我很高兴能帮上忙:)
  • @Christos 您应该使用正确的 HttpClient 和消息处理程序示例更新您的答案,因为没有 IHttpClient。
猜你喜欢
  • 2019-12-09
  • 1970-01-01
  • 2020-11-04
  • 1970-01-01
  • 2021-01-16
  • 2012-02-12
  • 1970-01-01
  • 2020-03-10
  • 1970-01-01
相关资源
最近更新 更多