我无法通过遵循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>>();
答案 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的部分应该会有所帮助。