C# Mock IHttpclient & CreateClient

南楼画角 提交于 2020-12-05 12:01:54

问题


I have a function that I want to x-unit test, but it seems that I have to mock the CreateClient function? Whenever I debug it during testing it seems that the var client is equals to null. I am injecting the dependencies properly, I am sure of that. What I want to know is how to mock the CreateClient.

here is that function:

    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());
        }
    }

here is the unit test, I am mocking the IHttpClient in a separate class and I am using that class.

    [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));
        }
    }

it seems to me that I also need to mock the CreateClient class is it?


回答1:


You should inject a mocked object for ClientFactory for which you have setup the CreateClient method.

// 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);

Then you have to pass the httpClientFactoryMock.Object to the constructor:

var messageHistoryService = new MessageHistoryService(httpClientFactoryMock.Object);

Update

In order to unit test HttpClient since it hasn't any interface you should wrap it in way as it is described here.

Specifically we have to arrange the http client as below:

// 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/"),
};

Now we should return the above httpClient when CreateClient is called.

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

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


来源:https://stackoverflow.com/questions/54200046/c-sharp-mock-ihttpclient-createclient

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!