Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Create Mock Configuration.GetSection(“Section:SubSection”) for objects list

Objective

Create a mock object, using Moq and XUnit, for loading the specific section "Character/Skills" to enhance the coverage in unit testing.

The SUT (in some point), loads the setting in the way

var skills = Configuration.GetSection(“Character:Skills”);

From the following appSetting:

{
    "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..."
}

Previous Reading

Reading these posts (and other others, as result of Googling), I noticed that we can only use a primitive "string" datatype or else new Mock<IConfigurationSection> object (with no setting):

  • Stack Overflow - how to mock Configuration.GetSection(“foo:bar”),
  • Mocking IConfiguration extension method
  • Mocking IConfiguration Getvalue() extension method in Unit Test

Constraint: Copying the appSetting file into the TestProject (or create a MemoryStream) to load the real settings could solve this scenario, but the test would be a "Integration" instead of "Unit"; since there is an I/O dependency.

The approach

The code's idea (shown later) is mocking each property (key/id) and then merging them in a tree similar to this:

  • "Character" ------ Configuration to be read, using GetSection() and then Get<T>()
    • "Skills" ------ Configuration list with merged attribute
      • "Key" - "CQC Combat" ------ Primitive value 1
      • "Id" - "15465" ------ Primitive value 2

The Code

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);

Expected result

Running the original system, I get the instantiated list with its respective Here is the screenshot:

appsetting loaded sucessfully

Mocked result

The code above, I only get the instantiated list but all attributes return null. Here is the screenshot:

appsetting mocked with null values

like image 932
Cesar Urdaneta Avatar asked Sep 06 '25 23:09

Cesar Urdaneta


1 Answers

Finally I refactored the code, getting rid the whole foreach block and replacing the list initialization var mockConfSections = new List<IConfigurationSection>(); with the follow piece of code, which is simpler and cleaner.

var fakeSkillSettings = skillsConfiguration.SelectMany(
    skill => new Dictionary<string, string> {
        { $"Character:Skills:{skillsConfiguration.IndexOf(skill)}:Key", skill.Key },
        { $"Character:Skills:{skillsConfiguration.IndexOf(skill)}:Id" , skill.Id  },
});

var configBuilder = new ConfigurationBuilder();
var mockConfSections = configBuilder.AddInMemoryCollection(fakeSkillSettings)
    .Build()
    .GetSection("Character:Skills")
    .GetChildren();

Explanation

As the previous implementation built a configuration tree with mocked nodes, there was a need to build a setup and return for each one, resulting in a bloated solution.

Based on the article Keeping Configuration Settings in Memory, I projected the list with flattened Key/Id Dictionary using the LINQ SelectMany, then built the memory configuration and finally mocked the setting with "real nodes", resulting in one mock setup.

like image 153
Cesar Urdaneta Avatar answered Sep 09 '25 21:09

Cesar Urdaneta