【问题标题】:How to inject a service into a constructor when creating a new instance of an object?创建对象的新实例时如何将服务注入构造函数?
【发布时间】:2018-06-29 16:09:03
【问题描述】:

设置:我已经注册了一个从 appsettings.json 提取数据的配置服务,它工作正常。我还有一个控制器,它使用该服务从该文件中获取设置,这再次像它应该的那样工作:

public class ApiController : Controller
{
    private readonly string _apiUri;
    public ApiController(IOptions<Configurator> config)
    {
        _apiUri = config.Value.ApiSettings.ApiBaseUrl + 
                  config.Value.ApiSettings.ApiVersion;
    }
    //...
}

现在请注意,我是自动化单元测试和 asp.net 核心的新手。我想做的是解耦 ApiController 对注入服务的依赖,这样我就可以使用单独的 XUnit 测试项目来测试控制器内部的功能,类似于this tutorial 中的示例。

为此,我创建了一个模型和接口来表示我的 appsettings.json 文件的 ApiSettings 部分:

"ApiSettings": {
    "ApiBaseUrl": "https://example.com/api/",
    "ApiVersion": "v1/"
}

模型:

public class ApiSettings : IApiSettings
{
    public string ApiBaseUri { get; set; }
    public string ApiVersion { get; set; }
}

界面:

public interface IApiSettings
{
    string ApiBaseUri { get; set; }
    string ApiVersion { get; set; }
}

然后我创建了一个依赖于注入设置的服务的类:

public class ApiSettingsBuilder
{
    private readonly string _apiUri;
    public ApiSettingsBuilder(IOptions<Configurator> config)
    {
        _apiUri = config.Value.ApiSettings.ApiBaseUrl + 
                  config.Value.ApiSettings.ApiVersion;
    }

    public string ApiUri { get { return _apiUri; } }
}

问题:如何创建此类的新实例?

public class ApiController : Controller
{
    private readonly string _apiUri;
    public ApiController()
    {
        ApiSettingsBuilder builder = new ApiSettingsBuilder(/*What do I do here*/);
        _apiUri = builder.ApiUri;
    }

    public ApiController(IApiSettings settings)
    {
        //For testing
        _apiUri = settings.ApiBaseUrl + settings.ApiVersion;
    }
    //...
} 

另外,我知道这有点矫枉过正,但我​​仍然想要一个答案,因为它可能在其他情况下有用。

【问题讨论】:

    标签: unit-testing asp.net-core dependency-injection asp.net-core-mvc asp.net-core-2.0


    【解决方案1】:

    您不必为单元测试目的创建新类,您可以使用适当的框架模拟 IOptions 的接口,例如起订量:

    var configurator = new Configurator() { ApiBaseUrl = "abc" };
    var mock = new Mock<IOptions<Configurator>>();
    mock.Setup(ap => ap.Value).Returns(configurator);
    

    然后您可以将模拟对象传递给您的构造函数进行单元测试:

    var controller = new ApiController(mock.Object);
    

    【讨论】:

    • 谢谢!我不知道如何去模拟服务。我想我对控制器“解耦”的想法也是不必要的,因为这就是注入服务的作用——我只是没有考虑实际发生了什么。
    • 小技巧,你可以使用Options.Create(...)从一个T中制作一个IOptions
    猜你喜欢
    • 2013-11-17
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 2018-01-10
    • 1970-01-01
    • 2011-01-06
    • 2016-11-19
    • 1970-01-01
    相关资源
    最近更新 更多