For some time I am trying to make a mocking library for C# that mocks objects with just 1 line of code. I am using abstract factory pattern to know what is the object's type so that I can work with concrete factories.
I have the following Abstract factory class which I am not very happy with.
public class MockContext<T>
{
public T CreateMockObject()
{
IFactory<T> factory = null;
if (typeof(T).IsPrimitive || typeof(T) == typeof(string) || typeof(T) == typeof(decimal))
{
factory = new PrimitiveFactory<T>();
}
else if (typeof(T).IsArray)
{
factory = new ArrayFactory<T>();
}
else if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
factory = new CollectionFactory<T>();
}
else if (typeof(T).IsClass && typeof(T) != typeof(string))
{
factory = new ClassFactory<T>();
}
return factory.Create();
}
}
}
Which gets called by the following class
public class Mocker
{
public T MockObject<T>()
{
MockContext<T> context = new MockContext<T>();
T mockObject = context.CreateMockObject();
return mockObject;
}
}
This abstract factory instantiates other factories depending on of the object.
Here is my class factory:
public class ClassFactory<T> : IFactory<T>
{
public T Create()
{
T mockObject = (T)Activator.CreateInstance(typeof(T));
IEnumerable<PropertyInfo> properties = typeof(T).GetProperties().Where(p => p.CanWrite);
GenericMethodInvokerUtil method = new GenericMethodInvokerUtil();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(T))
throw new Exception("Circular properties are not supported!");
method.InvokeMockObject<T>(property.PropertyType, property, mockObject);
}
return mockObject;
}
}
Here is my Primitive factory:
public class PrimitiveFactory<T> : IFactory<T>
{
private readonly Dictionary<Type, IPrimitiveFactory<T>> _primitiveFactories = new Dictionary<Type, IPrimitiveFactory<T>>()
{
{ typeof(string), new StringFactory() as IPrimitiveFactory<T> },
{ typeof(bool), new BooleanFactory() as IPrimitiveFactory<T> },
{ typeof(byte), new ByteFactory() as IPrimitiveFactory<T> },
{ typeof(sbyte), new SbyteFactory() as IPrimitiveFactory<T> },
{ typeof(char), new CharFactory() as IPrimitiveFactory<T> },
{ typeof(decimal), new DecimalFactory() as IPrimitiveFactory<T> },
{ typeof(double), new DoubleFactory() as IPrimitiveFactory<T> },
{ typeof(float), new FloatFactory() as IPrimitiveFactory<T> },
{ typeof(int), new IntFactory() as IPrimitiveFactory<T> },
{ typeof(uint), new UintFactory() as IPrimitiveFactory<T> },
{ typeof(long), new LongFactory() as IPrimitiveFactory<T> },
{ typeof(ulong), new UlongFactory() as IPrimitiveFactory<T> },
{ typeof(short), new ShortFactory() as IPrimitiveFactory<T> },
{ typeof(ushort), new UshortFactory() as IPrimitiveFactory<T> },
{ typeof(object), new ObjectFactory() as IPrimitiveFactory<T> },
};
public T Create()
{
if (_primitiveFactories.ContainsKey(typeof(T)))
{
IPrimitiveFactory<T> primitiveFactory = _primitiveFactories[typeof(T)];
return primitiveFactory.Create();
}
throw new KeyNotFoundException($"The key of type: {typeof(T)} is not found.");
}
}
All primitive types has their own factories which is determined by the type of the object.
Array factory:
public class ArrayFactory<T> : IFactory<T>
{
public T Create()
{
var genericMethodInvoker = new GenericMethodInvokerUtil();
Array array = Activator.CreateInstance(typeof(T), new object[] { RandomUtil.Instance.Next(1, 100) }) as Array;
for (int i = 0; i < array.Length; i++)
{
Type arrayElementType = typeof(T).GetElementType();
array.SetValue(genericMethodInvoker.InvokeMockObject<T>(arrayElementType), i);
}
return (T)Convert.ChangeType(array, typeof(T));
}
}
Collection factory
public class CollectionFactory<T> : IFactory<T>
{
public T Create()
{
ICollectionFactory<T> factory = null;
if (typeof(IDictionary).IsAssignableFrom(typeof(T)))
{
factory = new DictionaryFactory<T>() as ICollectionFactory<T>;
}
else if (typeof(IList).IsAssignableFrom(typeof(T)))
{
factory = new ListFactory<T>() as ICollectionFactory<T>;
}
return factory.Create();
}
}
Collections are on the other hand are separated in tow Dictionary and List
public class DictionaryFactory<T> : ICollectionFactory<T>
{
public T Create()
{
GenericMethodInvokerUtil methodInvoker = new GenericMethodInvokerUtil();
IDictionary dictionary = (IDictionary)Activator.CreateInstance(typeof(T));
int numberOfElements = RandomUtil.Instance.Next(1, 100);
Type[] arguments = dictionary.GetType().GetGenericArguments();
Type keyType = arguments[0];
Type valueType = arguments[1];
for (int i = 0; i < numberOfElements; i++)
{
object key = methodInvoker.InvokeMockObject<object>(keyType);
object value = methodInvoker.InvokeMockObject<object>(valueType);
if (!dictionary.Contains(key))
{
dictionary.Add(key, value);
}
}
return (T)dictionary;
}
}
And List factory
public class ListFactory<T> : ICollectionFactory<T>
{
public T Create()
{
GenericMethodInvokerUtil methodInvoker = new GenericMethodInvokerUtil();
IList list = (IList)Activator.CreateInstance(typeof(T));
int numberOfElements = RandomUtil.Instance.Next(1, 100);
Type valueType = list.GetType().GetGenericArguments()[0];
for (int i = 0; i < numberOfElements; i++)
{
object value = methodInvoker.InvokeMockObject<object>(valueType);
list.Add(value);
}
return (T)list;
}
}
I am using the above code as follows:
static void Main(string[] args)
{
Mocker mocker = new Mocker();
var mock = mocker.MockObject<Test>();
}
With Test being some class with properties.
Mock object gets called recursively from the following utility class:
public class GenericMethodInvokerUtil
{
public object InvokeMockObject<T>(Type type, PropertyInfo property, object currentObject)
{
object concreteResult = GetMockObjectResult(type);
property.SetValue(currentObject, concreteResult);
return currentObject;
}
public object InvokeMockObject<T>(Type type)
{
object concreteResult = GetMockObjectResult(type);
return concreteResult;
}
private object GetMockObjectResult(Type type)
{
MethodInfo method = typeof(Mocker).GetMethod("MockObject", new Type[0] { });
MethodInfo generic = method.MakeGenericMethod(type);
object instance = Activator.CreateInstance(typeof(Mocker));
object result = generic.Invoke(instance, null);
object concreteResult = Convert.ChangeType(result, type);
return concreteResult;
}
}
Can anyone suggest how to write this better?
Maybe there is a pattern that I am not familiar with. Any help would be appreciated.
You can find the complete source of the project in my github HERE.