Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用定制的HttpClientHandler配置来单元测试/依赖注入依赖于HttpClient的类_C#_Unit Testing_Dependency Injection_Inversion Of Control - Fatal编程技术网

C# 如何使用定制的HttpClientHandler配置来单元测试/依赖注入依赖于HttpClient的类

C# 如何使用定制的HttpClientHandler配置来单元测试/依赖注入依赖于HttpClient的类,c#,unit-testing,dependency-injection,inversion-of-control,C#,Unit Testing,Dependency Injection,Inversion Of Control,我正在寻找关于如何改进当前设计的建议,以测试一个依赖于HttpClient和自定义HttpClientHandler配置的类(下面的示例)。我通常使用构造函数注入来注入在整个应用程序中保持一致的HttpClient,但是因为它位于类库中,我不能依靠库的使用者来正确设置HttpClientHandler 对于测试,我遵循在HttpClient构造函数中替换HttpClientHandler的标准方法。因为我不能依赖库的使用者来注入有效的HttpClient,所以我没有将其放入公共构造函数中,而是使

我正在寻找关于如何改进当前设计的建议,以测试一个依赖于
HttpClient
和自定义
HttpClientHandler
配置的类(下面的示例)。我通常使用构造函数注入来注入在整个应用程序中保持一致的HttpClient,但是因为它位于类库中,我不能依靠库的使用者来正确设置
HttpClientHandler

对于测试,我遵循在
HttpClient
构造函数中替换
HttpClientHandler
的标准方法。因为我不能依赖库的使用者来注入有效的
HttpClient
,所以我没有将其放入公共构造函数中,而是使用带有内部静态方法(
CreateWithCustomHttpClient()
)的私有构造函数来创建它。这背后的意图是:

  • 依赖项注入库不应自动调用私有构造函数。我知道,如果我将其公开/内部,那么一些已经注册了
    HttpClient
    的DI库将调用该构造函数
  • 内部静态方法可以由单元测试库使用
    InternalsVisibleToAttribute
这个设置对我来说似乎相当复杂,我希望有人能够提出改进建议,但我知道这可能是非常主观的,因此如果在这种情况下有任何既定的模式或设计规则可以遵循,我将非常感谢听到他们

我加入了
DownloadSomethingAsync()
方法,只是为了演示为什么
HttpClientHandler
需要非标准配置。默认情况下,重定向响应在不返回响应的情况下自动在内部重定向,我需要重定向响应,以便可以将其封装在一个类中,该类报告下载进度(的功能与此问题无关)

公共类DemoClass
{
私有静态只读HttpClient defaultHttpClient=新HttpClient(
新型HttpClientHandler
{
AllowAutoRedirect=false
});
专用只读ILogger记录器;
私有只读HttpClient HttpClient;
公共DemoClass(ILogger记录器):此(记录器,defaultHttpClient){}
私有DemoClass(ILogger记录器、HttpClient HttpClient)
{
this.logger=logger??抛出新的ArgumentNullException(nameof(logger));
this.httpClient=httpClient??抛出新的ArgumentNullException(nameof(httpClient));
}
[过时(“这仅用于测试,不应用于调用代码”)]
内部静态DemoClass CreateWithCustomHttpClient(ILogger记录器、HttpClient HttpClient)
=>新的DemoClass(记录器、httpClient);
公共异步任务下载SomeThingAsync(CancellationToken ct=default)
{
//生成请求
logger.LogInformation(“发送下载请求”);
HttpRequestMessage请求=新建HttpRequestMessage(HttpMethod.Get)http://example.com/downloadredirect");
//发送请求
HttpResponseMessage response=等待httpClient.SendAsync(请求,ct);
//分析结果
开关(响应状态代码)
{
案例HttpStatusCode。重定向:
打破
案例HttpStatusCode.no内容:
返回null;
默认值:抛出新的InvalidOperationException();
}
//获取重定向位置
Uri重定向=response.Headers.Location;
如果(重定向==null)
抛出新的InvalidOperationException(“重定向响应未包含重定向URI”);
//创建一个类来处理带有进度跟踪的下载
LogDebug(“包装发布下载请求”);
IDownloadController=新的HttpDownloadController(重定向);
//开始下载
LogDebug(“开始发布下载”);
返回wait controller.downloadsync();
}
}
在我看来,我将在中使用,并创建一个自定义依赖项注入扩展,供类库的使用者使用:

public static class DemoClassServiceCollectionExtensions
{
    public static IServiceCollection AddDemoClass(
        this IServiceCollection services, 
        Func<HttpMessageHandler> configureHandler = null)
    {
        // Configure named HTTP client with primary message handler
        var builder= services.AddHttpClient(nameof(DemoClass));

        if (configureHandler == null)
        {
            builder = builder.ConfigurePrimaryHttpMessageHandler(
                () => new HttpClientHandler
                {
                    AllowAutoRedirect = false
                });
        }
        else
        {
            builder = builder.ConfigurePrimaryHttpMessageHandler(configureHandler);
        }

        services.AddTransient<DemoClass>();

        return services;
    }
}
您可以要求使用者必须调用
AddDemoClass
才能使用
DemoClass

var services = new ServiceCollection();
services.AddDemoClass();
通过这种方式,您可以隐藏HTTP客户端构造的详细信息


同时,在测试中,您可以模拟
IHttpClientFactory
,返回
HttpClient
,以进行测试。

好方法。您可以稍微修改它,以使用类型化客户机,而不是像您所示的那样手动添加为瞬态。它为你做了所有的样板设置。我非常喜欢这种方法。不幸的是,IHttpClientFactory实际上与Extension.DependencyInjection()紧密耦合。我正在努力开发这个库,使所有使用Extensions.DependencyInjection和Autofac的内部团队都能轻松使用它,并为根本不使用DI的项目提供良好的默认实现。这是非常具体的我的需要,我在问题中没有详细说明,所以这个答案对我来说仍然有效。@Brad在这种情况下,你仍然可以借用这个想法。类似于为实例化
DemoService
提供工厂。
var services = new ServiceCollection();
services.AddDemoClass();