Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.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#创建模拟配置。对象列表的GetSection(“;Section:SubSection”;) 客观的_C#_Unit Testing_Mocking_Moq_Xunit - Fatal编程技术网

C#创建模拟配置。对象列表的GetSection(“;Section:SubSection”;) 客观的

C#创建模拟配置。对象列表的GetSection(“;Section:SubSection”;) 客观的,c#,unit-testing,mocking,moq,xunit,C#,Unit Testing,Mocking,Moq,Xunit,使用Moq和XUnit创建一个模拟对象,用于加载特定部分“角色/技能”,以增强单元测试中的覆盖率 SUT(在某些情况下)以以下方式加载设置 var skills = Configuration.GetSection(“Character:Skills”); 从以下应用程序设置: { "dummyConfig1": { "Description": "bla bla bla...", }, &qu

使用Moq和XUnit创建一个模拟对象,用于加载特定部分“角色/技能”,以增强单元测试中的覆盖率

SUT(在某些情况下)以以下方式加载设置

var skills = Configuration.GetSection(“Character:Skills”);
从以下应用程序设置:

{
    "dummyConfig1": {
        "Description": "bla bla bla...",
    },
    "Character": {
    "Name": "John Wick",
    "Description": "A retired hitman seeking vengeance for the killing of the dog given to him...",
    "Skills": [
        {
        "Key": "CQC Combat",
        "Id": "15465"
        },
        {
        "Key": "Firearms",
        "Id": "14321"
        },
        {
        "Key": "Stealth",
        "Id": "09674"
        },
        {
        "Key": "Speed",
        "Id": "10203"
        }
    ],
    "DummyConf2": "more bla bla bla..."
}
先前的阅读 在阅读这些帖子(以及谷歌搜索的其他帖子)时,我注意到我们只能使用基本的“字符串”数据类型或新的模拟对象(没有设置):

  • ,
约束:将appSetting文件复制到TestProject(或创建MemoryStream)以加载实际设置可以解决此问题,但测试将是“集成”而不是“单元”;因为存在I/O依赖关系

方法 代码的思想(稍后显示)是模拟每个属性(key/id),然后将它们合并到一个类似以下内容的树中:

  • “Character”--要读取的配置,使用
    GetSection()
    然后使用
    Get()
    • “技能”---具有合并属性的配置列表
      • “键”-“CQC战斗”--原语值1
      • “Id”-“15465”--原语值2
代码
var skillsConfiguration=新列表
{
新技能配置{Key=“CQC战斗”,Id=“15465”},
新技能配置{Key=“firms”,Id=“14321”},
新技能配置{Key=“Stealh”,Id=“09674”},
新技能配置{Key=“Speed”,Id=“10203”},
};
var configurationMock=new Mock();
var mockConfSections=新列表();
foreach(技能配置中的var技能)
{
var指数=技能配置。指数(技能);
//设置键字符串值
var mockConfSectionKey=new Mock();
Setup(s=>s.Path).Returns($“Character:Skills:{index}:Key”);
设置(s=>s.Key)。返回(“Key”);
设置(s=>s.Value).Returns(skill.Key);
//设置Id字符串值
var mockConfSectionId=new Mock();
Setup(s=>s.Path).Returns($“Character:Skills:{index}:Id”);
mockConfSectionId.Setup(s=>s.Key).Returns(“Id”);
设置(s=>s.Value).Returns(skill.Id);
//将属性“key/id”合并为配置节列表
var mockConfSection=new Mock();
Setup(s=>s.Path).Returns($“Character:Skills:{index}”);
Setup(s=>s.Key).Returns(index.ToString());
Setup(s=>s.GetChildren()).Returns(新列表{mockConfSectionKey.Object,mockConfSectionId.Object});
//添加具有合并属性的技能对象
添加(mockConfSection.Object);
}
//添加技能列表
var skillsMockSections=new Mock();
设置(cfg=>cfg.Path).Returns(“Character:Skills”);
skillsMockSections.Setup(cfg=>cfg.Key).Returns(“Skills”);
设置(cfg=>cfg.GetChildren())。返回(mockConfSections);
//模拟整个部分,以便在SUT中使用GetSection()方法
configurationMock.Setup(cfg=>cfg.GetSection(“Character:Skills”)).Returns(skillsMockSections.Object);
预期结果 运行原始系统,我得到实例化的列表及其各自的 以下是截图:

模拟结果 在上面的代码中,我只得到实例化的列表,但所有属性都返回null。 以下是截图:


最后我重构了代码,去掉了整个
foreach
块,并替换了列表初始化
var mockConfSections=new list()与下面的代码片段,这是更简单和更干净

var fakeSkillSettings=skillsConfiguration.SelectMany(
skill=>新字典{
{$“字符:技能:{skillscoConfiguration.IndexOf(skill)}:Key”,skill.Key},
{$”字符:技能:{skillscoConfiguration.IndexOf(skill)}:Id',skill.Id},
});
var configBuilder=new ConfigurationBuilder();
var mockConfSections=configBuilder.AddInMemoryCollection(fakeSkillSettings)
.Build()
.GetSection(“角色:技能”)
.GetChildren();
解释 由于前面的实现使用模拟节点构建了一个配置树,因此需要为每个节点构建一个设置并返回,从而导致解决方案过于臃肿


根据这篇文章,我使用展开的键/Id字典投影列表,然后构建内存配置,最后使用“真实节点”模拟设置,生成一个模拟设置。

为什么不使用并设置一个普通键值配置?因为它实现了MemoryStream,因此将是一个“集成测试”而不是“单位”;因为存在I/O依赖关系。我想使用mocked vaules。它实现
IConfigurationSource
接口并继承
对象
,除了name@PavelAnikhouski基于你的洞察力,我只回答我自己。谢谢。让你的类依赖于
IOptions
,而不是
IConfiguration
,那么你就不需要模仿它了。;)
var skillsConfiguration = new List<SkillsConfig>
{
    new SkillsConfig { Key = "CQC Combat"   , Id = "15465" },
    new SkillsConfig { Key = "Firearms"     , Id = "14321" },
    new SkillsConfig { Key = "Stealh"       , Id = "09674" },
    new SkillsConfig { Key = "Speed"        , Id = "10203" },
};

var configurationMock = new Mock<IConfiguration>();
var mockConfSections = new List<IConfigurationSection>();

foreach (var skill in skillsConfiguration)
{
    var index = skillsConfiguration.IndexOf(skill);

    //Set the Key string value
    var mockConfSectionKey = new Mock<IConfigurationSection>();
    mockConfSectionKey.Setup(s => s.Path).Returns($"Character:Skills:{index}:Key");
    mockConfSectionKey.Setup(s => s.Key).Returns("Key");
    mockConfSectionKey.Setup(s => s.Value).Returns(skill.Key);

    //Set the Id string value
    var mockConfSectionId = new Mock<IConfigurationSection>();
    mockConfSectionId.Setup(s => s.Path).Returns($"Character:Skills:{index}:Id");
    mockConfSectionId.Setup(s => s.Key).Returns("Id");
    mockConfSectionId.Setup(s => s.Value).Returns(skill.Id);

    //Merge the attribute "key/id" as Configuration section list
    var mockConfSection = new Mock<IConfigurationSection>();                
    mockConfSection.Setup(s => s.Path).Returns($"Character:Skills:{index}");
    mockConfSection.Setup(s => s.Key).Returns(index.ToString());
    mockConfSection.Setup(s => s.GetChildren()).Returns(new List<IConfigurationSection> { mockConfSectionKey.Object, mockConfSectionId.Object });    
    
    //Add the skill object with merged attributes
    mockConfSections.Add(mockConfSection.Object);
}

// Add the Skill's list
var skillsMockSections = new Mock<IConfigurationSection>();
skillsMockSections.Setup(cfg => cfg.Path).Returns("Character:Skills");
skillsMockSections.Setup(cfg => cfg.Key).Returns("Skills");
skillsMockSections.Setup(cfg => cfg.GetChildren()).Returns(mockConfSections);

//Mock the whole section, for using GetSection() method withing SUT
configurationMock.Setup(cfg => cfg.GetSection("Character:Skills")).Returns(skillsMockSections.Object);