【问题标题】:http typed client in net core unit testing网络核心单元测试中的http类型客户端
【发布时间】:2019-06-10 21:38:52
【问题描述】:

我将如何对调用 http 类型客户端的服务进行单元测试? https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2#typed-clients 给出的示例不使用接口。

public GitHubService(HttpClient client)

使用 xunit/moq 进行单元测试是否需要为类型化客户端创建接口?或者我不需要对这个服务进行单元测试。

【问题讨论】:

    标签: asp.net-core


    【解决方案1】:

    如果您的服务类设计得当,那么单元测试就没有多大意义。您的方法几乎应该只是封装对HttpClient 的调用,抽象 URLs/headers/connection details/etc。您可以合理地确定HttpClient 在一般意义上有效,因此基本上没有要测试的真实代码。同样,这是假设你做的事情是正确的。

    如果你确实需要更复杂的逻辑,那么你应该有一个将这个简单的服务类作为依赖的类,复杂的逻辑应该去那里。你的服务类可以实现一个接口,所以你最好去那个时候。

    您当然可以对您的服务类进行集成测试,这将确保它按应有的方式运行,实际调用 API 或其传真。

    【讨论】:

    • 是的,我的方法基本上只是调用HttpClient,我想我更多的是考虑当我通过 DI 将服务类注入另一个服务时,然后当我开始测试该服务时我现在必须给它提供没有接口的http服务。
    • 让这个服务类实现一个接口,然后你可以在一个 mock 中子代任何依赖它的东西。包含 HttpClient 依赖项(这是重点),因此使用此服务的东西不知道也不关心它。
    【解决方案2】:

    我不明白你的意思

    http 类型的客户端

    但是如果像示例中一样,您想测试一个使用 HttpClient 的类,您可以为 HttpClient 创建一个包装器并使用依赖注入传递它的接口(以便您可以模拟它),或者您利用 HttpResponseMessage HttpClient 的构造函数参数。

    将 HttpClient 设为构造函数参数,并在测试中创建如下代码:

    var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
    mockHttpMessageHandler.Protected()
       .Setup<Task<HttpResponseMessage>>(
          "SendAsync",
          ItExpr.IsAny<HttpRequestMessage>(), // Customise this as you want
          ItExpr.IsAny<CancellationToken>()
       )
       // Create the response you want to return
       .ReturnsAsync(new HttpResponseMessage()
       {    
          StatusCode = HttpStatusCode.OK,
          Content = new StringContent("[{'prop1': 100,'prop2': 'value'}]"),
       });
    
    // Create an HttpClient using the mocked message handler
    var httpClient = new HttpClient(mockHttpMessageHandler.Object)
    {
       BaseAddress = new Uri("http://anyurl.com/"),
    };
    
    var testedService = new MyServiceUnderTest(httpClient);
    
    var result = await testedService.MethodUnderTest(parameters [...]);
    

    为了简化起订量的设置,限制预期的 HttpRequestMessage,我使用了这个辅助方法。

        /// <summary>
        /// Setup the mocked http handler with the specified criteria
        /// </summary>
        /// <param name="httpStatusCode">Desired status code returned in the response</param>
        /// <param name="jsonResponse">Desired Json response</param>
        /// <param name="httpMethod">Post, Get, Put ...</param>
        /// <param name="uriSelector">Function used to filter the uri for which the setup must be applied</param>
        /// <param name="bodySelector">Function used to filter the body of the requests for which the setup must be applied</param>
        private void SetupHttpMock(HttpStatusCode httpStatusCode, string jsonResponse, HttpMethod httpMethod, Func<string, bool> uriSelector, Func<string, bool> bodySelector = null)
        {
            if (uriSelector == null) uriSelector = (s) => true;
            if (bodySelector == null) bodySelector = (s) => true;
    
            _messageHandlerMock
                .Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync",
                    ItExpr.Is<HttpRequestMessage>(m =>
                        m.Method == httpMethod &&
                        bodySelector(m.Content.ReadAsStringAsync().Result) &&
                        uriSelector(m.RequestUri.ToString())),
                    ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(new HttpResponseMessage
                {
                    StatusCode = httpStatusCode,
                    Content = jsonResponse == null ? null : new StringContent(jsonResponse, Encoding.UTF8, "application/json")
                });
        }
    

    【讨论】:

    • "类型化客户端 类型化客户端提供与命名客户端相同的功能,而无需使用字符串作为键。类型化客户端方法在使用客户端时提供 IntelliSense 和编译器帮助。它们提供单一位置来配置和与特定的 HttpClient 交互。例如,单个类型的客户端可能用于单个后端端点并封装处理该端点的所有逻辑。另一个优点是它们与 DI 一起使用并且可以在您的应用程序中需要时注入。"
    猜你喜欢
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    相关资源
    最近更新 更多