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..."
}
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):
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 code's idea (shown later) is mocking each property (key/id) and then merging them in a tree similar to this:
GetSection()
and then Get<T>()
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);
Running the original system, I get the instantiated list with its respective Here is the screenshot:
The code above, I only get the instantiated list but all attributes return null. Here is the screenshot:
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();
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With