在单元测试中获取配置

时间:2020-05-26 17:18:41

标签: c# xunit options appsettings

我无法通过遵循IOptions模式来弄清楚我正在使用的某些配置。

我要测试的服务是下一个:

        private readonly Dictionary<ErrorType, ErrorObject> _errors;

        public UserService(
            ...(Many services)...
            IOptions<Dictionary<ErrorType, ErrorObject>> errors)
        {
            ...
            _errors = errors.Value;
        }

其中ErrorType是一个Enum和ErrorObject,是一个对象,其格式与我在appsettings.json中的错误格式相同。就是这样:

{
  "Errors": {
    "UserNotFound": {
      "ErrorCode": 101,
      "Message": "User not found"
    },
    "WrongPassword": {
      "ErrorCode": 102,
      "Message": "Wrong password"
    }
  }
}

所以这工作得很好,但是我现在的问题是我不知道如何实例化,模拟它或将其注入到我的测试类中,因为即使我注入了它也无法在这样的情况下解决它课,我总是得到一个空值。 我将它这样注入到测试夹具中(与我的实际注入模块相同):

serviceCollection.Configure<Dictionary<BusinessErrorType, BusinessErrorObject>>(x => Configuration.GetSection("Errors").Bind(x));

我必须正确地注入所有错误的IConfiguration,我只是无法为构造函数创建所需的Dictionary。

在这里我得到的错误没有任何问题:

IConfiguration Configuration = ioCModule.ServiceProvider.GetRequiredService<IConfiguration>();
            var errorsConfig = Configuration.GetSection("Errors");

现在我要弄清楚要在此声明中加上什么:

var _options = Options.Create<Dictionary<BusinessErrorType, BusinessErrorObject>>();

1 个答案:

答案 0 :(得分:3)

我以这种方式将其注入测试夹具中(与我的真实注入模块相同):

您尝试注入Dictionary,而您的班级希望获得Options。尝试注册和注入Options,很可能会成功。

现在我要弄清楚要在此声明中加上什么:

Options对象只是一些随时可用的值的持有者,或者 没有。您必须通过以下两种方法构造Options对象: 它是值,或者什么都不给(最后是空选项) 目的)。如果您使用不带任何参数的Options.Create<Dict>(),则会创建一个空对象,仅此而已。只需在此之前创建字典并将其传递给Options 装饰工。

var dict = new Dictionary<BusinessErrorType, BusinessErrorObject>();
dict.Add("UserNotFound", new BusinessErrorObject(....));
dict.Add("WrongPassword", new BusinessErrorObject(....));

var _options = Options.Create<Dictionary<BusinessErrorType, BusinessErrorObject>>(dict);
// now you have it: _options with some data inside

如果您希望将Bind()与Options一起使用-我怀疑它是否会那样工作,因为Bind用于从设置文件中读取配置来填充数据结构,而Options只是一个shell,类似“ Nullable <>”,它的存在只是为了传递信息。但是,我想您可以先创建一个字典,然后用配置(即Bind)填充它,然后用Options <>包裹刚刚填充的字典,然后然后在IoC中注册该Options对象,以便您的UserService可以获取其配置。。但是,我不确定100%绑定是否足够智能以填充通用词典。您需要自己进行测试,很遗憾,我手头没有任何aspnetcore项目可以使用

编辑/旁注:

您在 UNIT TEST 中建立/设置整个IoC感到很惊讶。创建单元测试时,您希望尽可能地剪切/模拟依赖项。这意味着没有IoC,它会给测试带来另一组问题/失败/等。

我的意思是,我希望您的单元测试看起来像这样:

[Test]
public void FoobarizingTheBaz()
{
    var mock1 = ....;
    var mock2 = ....;
    var mock3 = ....;
    var ... = ....;
    var mockN = ....;

    var dict = new Dictionary<....>();
    dict.Add(....);
    dict.Add(....);
    var mockOptions = Options.Create<...>(dict);

    var tested = new UserService(
        mock1, mock2, mock3, ... mockN, mockOptions
    );

    var result = tested.FoobarizeTheBaz();

    // assert...
}

那是单元测试。仅实例化被测试的对象,其余对象被模拟,并且您仅提供绝对必要的对象。当然,我不得不编写大量的setup / mock / etc,而不是依靠IoC自动实现它们,但是一旦编写了模拟,您就可以将它们带到一些通用代码,测试类init,fixture等上。并重复使用它们。

我的意思是,没有appsettings.json。没有IConfiguration。没有IoC等。

如果您想测试有多少组件相互交互,那么它更像是一个集成测试。在这种情况下,您可能希望看到this article在集成测试设置中使用自定义appsettings.json和IOptions进行触摸。但是,请记住,aspnetcore中的“集成测试”通常意味着测试您的服务如何响应请求并检查响应,因此您将在本文中看到。但是,有关IOptions的部分应该会有所帮助。

相关问题