Well the title's actually a bit misleading. Here we're talking about testing your public types. Nothing special there as we all do that because we're good little programmers, right? Yes I knew you did... The catch comes out when there's a good reason that you don't want consumers of your code just instantiating instances of particular types all willy nilly like. This is not your every day business types we write most of the time that we're focusing on, these are your frameworks and infrastructure and the like.
i.e. Your User Credentials type or a TimeStamping class.
These are classes that have set and expected behavior, tend to be sealed, and only internally created in your application frameworks but are used to facilitate common knowledge.
Well you have two traditional approaches to deal with this, neither of which is satisfactory:
- Make your constructors public (YUCK)
- Don't unit test them (NEVER an acceptable solution)
Thankfully the .Net Framework is a well designed system and therefore we have additional options, the likes of Friend Assemblies and Reflection comes to mind. As Friend Assemblies are not supported across all languages (not everyone is a C#'er) that leaves just reflection to work with.
Instead of manually cooking a solution each and ever time you need to accomplish this you can simply use this snippet to proxy access to the constructor(s) of your type. It works against constructors private and public, matches the supplied arguments to the most correct constructor, supports polymorphic behavior, adapts itself to the exception behavior of the underlying type (no need to trap TargetInvocationException), and actually could be used as a springboard to a light-weight DI mechanism if you were so inclined.
public T CreateInstance(Object[] arguments)
{
foreach (ConstructorInfo constructor in typeof(T).GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
ParameterInfo[] parameters = constructor.GetParameters();
if (arguments.Length != parameters.Length) continue;
for (Int32 i = 0; i < arguments.Length; i++)
{
if (!parameters[i].ParameterType.IsValueType && arguments[i] == null) continue;
if (parameters[i].ParameterType.IsValueType && arguments[i] == null || !parameters[i].ParameterType.IsAssignableFrom(arguments[i].GetType())) goto NextItem;
}
try
{
return (T)constructor.Invoke(arguments);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
NextItem:
continue;
}
throw new MissingMethodException("Cannot find suitable constructor");
}
note: My personal suggestion is that you utilize method overloading for your various constructor flavors to assist in maintenance. Just allow your strongly typed wrappers to delegate the work to the above method and you will be in that happy code making place!
|
|
James Zimmerman is a software artisan currently plotting to take over the planet. Oh yeah occasionally he shows up to his office to do work as well...
|
|