I have a tricky issue.
I'm writing a tool where a user can pass the source code of a C# model as input and it will return mockups (n instances with random content).
The question is: Based on this implementation, what are your suggestions to fix the issue described below, or eventally what other solution or framework would you propose instead?
I created a dummy class for testing it and the result is inconsistant: it correctly generates random values but country does not match the address, fullname does not march firstname + lastname etc..
The idea of the below code is that the class is not known at compilation time because the class to mockup is initially passed as input text). Using Roslyn, I dynamically compile and load it, then pass it to bugus. For clarity, I'm not exposing the code that calls CSharpCompilation.Create.
In Bogus, If I know in advance the name of the fields, I can do something like this:
.RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName(u.Gender))
.RuleFor(u => u.LastName, (f, u) => f.Name.LastName(u.Gender))
.RuleFor(u => u.UserName, (f, u) => f.Internet.UserName(u.FirstName, u.LastName))
But In my case, I'm processing the fields one by one and I don't know if they are correlated. For instance, If I process a field named UserName, I eventually don't know if the class has 2 other fields named FirstName and LastName and if they have been already mockuped.
Here is the code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Bogus;
namespace MockupEngine
{
public class MockupGenerator
{
private int _rowId = 0;
public List<object> GenerateMockup<T>(int count) where T : class, new()
{
_rowId = 0;
var faker = new Faker<T>()
.StrictMode(false)
.Rules((f, o) =>
{
foreach (var property in o.GetType().GetProperties())
{
FillProperyData(f, o, property);
}
});
var mockupList = new List<object>();
for (int i = 0; i < count; i++)
{
var mockup = faker.Generate();
mockupList.Add(mockup);
}
return mockupList;
}
private void FillProperyData<T>(Faker f, T o, PropertyInfo property)
{
string propertyName = property.Name;
if (property.GetCustomAttribute<ObsoleteAttribute>() != null)
{
return;
}
if (FieldSynonymous.Synonymous.ContainsKey(propertyName))
{
FillProperyDataForDictionaryEntries(f, o, property, FieldSynonymous.Synonymous[propertyName]);
return;
}
if (property.PropertyType == typeof(bool))
{
property.SetValue(o, f.Random.Bool());
}
if (property.PropertyType == typeof(int))
{
property.SetValue(o, f.Random.Int());
}
else if (property.PropertyType == typeof(string))
{
property.SetValue(o, f.Random.AlphaNumeric(10));
}
else if (property.PropertyType == typeof(DateTime))
{
property.SetValue(o, f.Date.Past());
}
else if (property.PropertyType == typeof(double))
{
property.SetValue(o, f.Random.Double());
}
// add more types as needed
}
private void FillProperyDataForDictionaryEntries<T>(Faker f, T o, PropertyInfo property, string propertyName)
{
object? result = null;
switch (propertyName)
{
case "Id":
result = _rowId++;
break;
case "Address":
result = f.Address.FullAddress();
break;
case "Street":
result = f.Address.StreetName();
break;
case "BuildingNumber":
result = f.Address.BuildingNumber();
break;
case "ZipCode":
result = f.Address.ZipCode();
break;
case "City":
result = f.Address.City();
break;
case "Country":
result = f.Address.Country();
break;
case "CountryCode":
result = f.Address.CountryCode();
break;
case "Price":
result = f.Commerce.Price();
break;
case "Categories":
result = f.Commerce.Categories(1).First();
break;
case "ProductName":
result = f.Commerce.ProductName();
break;
case "Company":
result = f.Company.CompanyName();
break;
case "Date":
result = f.Date.Past(2);
break;
case "Month":
result = f.Date.Month();
break;
case "EmailAddress":
result = f.Internet.ExampleEmail();
break;
case "AccountName":
result = f.Internet.ExampleEmail();
break;
case "Currency":
result = f.Finance.Currency();
break;
case "CreditCardNumber":
result = f.Finance.CreditCardNumber();
break;
case "IpAddress":
result = f.Internet.IpAddress();
break;
case "Password":
result = f.Internet.Password(10);
break;
case "Url":
result = f.Internet.Url();
break;
case "Text":
result = f.Random.AlphaNumeric(10);
break;
case "Lorem":
result = f.Lorem.Text();
break;
case "FirstName":
result = f.Name.FirstName();
break;
case "LastName":
result = f.Name.LastName();
break;
case "FullName":
result = f.Name.FullName();
break;
case "Prefix":
result = f.Name.Prefix();
break;
case "Suffix":
result = f.Name.Suffix();
break;
case "JobTitle":
result = f.Name.JobTitle();
break;
case "PhoneNumber":
result = f.Phone.PhoneNumber();
break;
case "FileName":
result = f.System.FileName();
break;
case "FileType":
result = f.System.FileType();
break;
case "FileExtension":
result = f.System.FileExt();
break;
default:
break;
}
if (result == null)
{
result = f.Lorem.Text();
}
ConvertResult(property, o, result);
}
private void ConvertResult<T>(PropertyInfo property, T o, object result)
{
Type resultType = result.GetType();
if (property.PropertyType == resultType)
{
property.SetValue(o, result);
return;
}
if (property.PropertyType == typeof(bool))
{
bool success = bool.TryParse(result.ToString(), out bool bResult);
if (success == true)
{
property.SetValue(o, bResult);
}
}
if (property.PropertyType == typeof(int))
{
bool success = int.TryParse(result.ToString(), out int iResult);
if (success == true)
{
property.SetValue(o, iResult);
}
}
else if (property.PropertyType == typeof(string))
{
property.SetValue(o, result.ToString());
}
else if (property.PropertyType == typeof(DateTime))
{
}
else if (property.PropertyType == typeof(double))
{
bool success = double.TryParse(result.ToString(), out double dResult);
if (success == true)
{
property.SetValue(o, dResult);
}
}
else
{
}
}
}
}
Synonymous is a dictionnary that allows me to guess what builtin bogus function to call based on the name of the field.
public static Dictionary<string, string> Synonymous = new Dictionary<string, string>
{
{"Id","Id" },
{"RowId","Id" },
{"Address", "Address"},
{"FullAddress", "Address"},
{"PrincipalAddress", "Address"},
{"BuildingAddress", "Address"},
{"Location", "Address"},
{"AddressLine1", "Address"},
{"AddressLine2", "Address"},
{"Address1", "Address"},
{"Address2", "Address"},
