I have below class and a dictionary of category item mappings
public class InfoClass
{
public InfoClass()
{
this.InfoID = string.Empty;
this.EDDetails = string.Empty;
this.EVDetails = string.Empty;
}
public int ID { get; set;}
public string InfoID { get; set;}
public string EDDetails { get; set; }
public string EVDetails { get; set; }
}
private static Dictionary<string, List<string>> ItemsMap = new Dictionary<string, List<string>>
{
{ Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
{ Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
};
I want to create the InfoClass mock object in such a way that, the InfoID of InfoClass should be random from the list inside the Dictionary. Based on that category of the selected InfoID, assign a value for EDDetails or EVDetials.
For example, If the random value of ReportId is "XYZ", then category of InfoID is ITEMCATEGORY_ED, then assign {Generate data based on a logic} value to EDDetails property and all the other properties EVDetails should be empty.
To achieve this, i tried creating a Customization using below.
public class InfoClassCustomization : ICustomization
{
private readonly Dictionary<string, List<string>> ItemsMap;
public ArchivedOrderLineItemInfoCustomization(Dictionary<string, List<string>> ItemsMap)
{
this.ItemsMap = ItemsMap;
}
public virtual void Customize(IFixture fixture)
{
fixture.Customize<InfoClass>(c => c
.Do(o =>
{
// Flatten your dictionary to a list of tuples
var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();
// Pick a random tuple
var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];
// Set the ReportID from the randomly selected tuple
o.ReportID = InfoIdPair.Value;
// Set the correct property based on the key of the randomly selected tuple
switch (InfoIdPair.Key)
{
case Constants.ITEMCATEGORY_ED:
o.EDDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
break;
case Constants.ITEMCATEGORY_ERANDRE:
o.EVDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
break;
}
}));
}
}
var fixture = new Fixture().Customize(new InfoClassCustomization(ItemsMap));
var obj = fixture.Create<InfoClass>();
But this didnt worked. Didn't even got any exception. So tried below
public static InfoClass GetItemInfo()
{
var fixture = new Fixture();
InfoClass o = new InfoClass();
// Flatten your dictionary to a list of tuples
var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();
// Pick a random tuple
var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];
// Set the ReportID from the randomly selected tuple
o.ReportID = InfoIdPair.Value;
// Set the correct property based on the key of the randomly selected tuple
switch (InfoIdPair.Key)
{
case Constants.ITEMCATEGORY_ED:
o.EDDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
break;
case Constants.ITEMCATEGORY_ERANDRE:
o.EVDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
break;
}
return o;
}
var fixture = new Fixture();
fixture.Customize<ArchivedOrderLineItemInfo>(c => c
.Do(o => o= GetItemInfo()));
var mockedvalue = fixture.Create<InfoClass>();
With the second approach, while i debug, i can see the data/values assigned to the properties inside the GetItemInfo method. But once it returned the object and assigned to "mockedvalue", the data generated inside the method is gone and a fresh mock data with out the custom logic is generated.
Is there any issue with my logic ? or am i missing something ?
Thinking of it other way, more from unit test perspective, I have modified your solution with ICUstomization
. Here is how I implemented it:
public class InfoClassCustomization : ICustomization
{
private readonly Dictionary<string, List<string>> ItemsMap;
public InfoClassCustomization(Dictionary<string, List<string>> ItemsMap)
{
this.ItemsMap = ItemsMap;
}
public void Customize(IFixture fixture)
{
// Flatten your dictionary to a list of tuples
var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();
// Pick a random tuple
var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];
// Set the ReportID from the randomly selected tuple
var reportID = InfoIdPair.Value;
var edDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
var evDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
fixture.Customize<InfoClass>(c =>
{
var composer = c.With(x => x.ReportID, reportID);
// Set the correct property based on the key of the randomly selected tuple
switch (InfoIdPair.Key)
{
case Constants.ITEMCATEGORY_ED:
return composer.With(x => x.EDDetails, edDetails);
case Constants.ITEMCATEGORY_ERANDRE:
return composer.With(x => x.EVDetails, evDetails);
default:
return c;
}
});
}
}
And here is sample unit test (I think you have missed call to Fixture
's Customize(new InfoClassCustomization(dict));
):
public class UnitTest1
{
[Fact]
public void Test1()
{
// Arrange
var dict = new Dictionary<string, List<string>>
{
{ Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
{ Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
};
var autoFixture = new Fixture()
.Customize(new InfoClassCustomization(dict));
// Act
var infoClass = autoFixture.Create<InfoClass>();
// Assert
var possibleReportIds = dict.Values.SelectMany(x => x);
Assert.NotNull(
possibleReportIds
.FirstOrDefault(x => x == infoClass.ReportID));
}
}
EDIT
Accordingly to comments, above method will create all objects with the same ReportId
. To correct that we can use Fixture.Register
method, as below:
public class InfoClassCustomization : ICustomization
{
private readonly Dictionary<string, List<string>> ItemsMap;
public InfoClassCustomization(Dictionary<string, List<string>> ItemsMap)
{
this.ItemsMap = ItemsMap;
}
public void Customize(IFixture fixture)
{
// Flatten your dictionary to a list of tuples
var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();
fixture.Register(() =>
{
// Pick a random tuple
var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];
// Set the ReportID from the randomly selected tuple
var reportID = InfoIdPair.Value;
var edDetails = "EDDetails";// JsonConvert.SerializeObject(fixture.Create<EdDetails>());
var evDetails = "EVDetails";// JsonConvert.SerializeObject(fixture.Create<EvDetails>());
return new InfoClass
{
ReportID = reportID,
EDDetails = edDetails,
EVDetails = evDetails,
ID = fixture.Create<int>(),
InfoID = fixture.Create<string>(),
};
});
}
}
Now below sample unit test passes:
public class UnitTest1
{
[Fact]
public void Test1()
{
var dict = new Dictionary<string, List<string>>
{
{ Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
{ Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
};
var autoFixture = new Fixture()
.Customize(new InfoClassCustomization(dict));
var infoClasses = autoFixture.CreateMany<InfoClass>(10);
var possibleReportIds = dict.Values.SelectMany(x => x).ToArray();
foreach (var ic in infoClasses)
{
Assert.True(possibleReportIds.Contains(ic.ReportID));
}
Assert.True(infoClasses.Select(ic => ic.ReportID).Distinct().Count() > 1);
}
}