Created
May 7, 2013 15:25
-
-
Save robertwilczynski/5533492 to your computer and use it in GitHub Desktop.
Extremely ugly C# constructor tester to catch lack of guard clauses for null/empty/whitespace values passed to constructor arguments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using Xunit.Sdk; | |
namespace SaaSOvation.IssueTrack.Core.Tests | |
{ | |
public class CtorTester | |
{ | |
public static object GetDefault(Type type) | |
{ | |
if (type.IsValueType) | |
{ | |
return Activator.CreateInstance(type); | |
} | |
return null; | |
} | |
public void Test<T>() | |
{ | |
foreach (var ctor in typeof (T).GetConstructors()) | |
{ | |
var args = ctor.GetParameters(); | |
for (int index = 0; index < args.Length; index++) | |
{ | |
var arg = args[index]; | |
var values = BuildArguments(typeof(T), args) | |
.ToArray(); | |
if (arg.ParameterType == typeof (string)) | |
{ | |
values[index] = ""; | |
var aex = AssertTargetInvocationThrows<ArgumentException, T>( | |
values, | |
arg.Name, | |
"empty", | |
x => x.ParamName == arg.Name); | |
values[index] = " "; | |
aex = AssertTargetInvocationThrows<ArgumentException, T>( | |
values, | |
arg.Name, | |
"whitespace", | |
x => x.ParamName == arg.Name); | |
} | |
values[index] = null; | |
var nex = AssertTargetInvocationThrows<ArgumentNullException, T>( | |
values, | |
arg.Name, | |
"null", | |
x => x.ParamName == arg.Name); | |
} | |
} | |
} | |
private TException AssertTargetInvocationThrows<TException, T>( | |
object[] values, | |
string arg, | |
string useCase, | |
Func<TException, bool> auxiliaryAssertion) | |
where TException : Exception | |
{ | |
TException ex = null; | |
try | |
{ | |
Activator.CreateInstance(typeof (T), values); | |
} | |
catch (TargetInvocationException tiex) | |
{ | |
if (tiex.InnerException != null && tiex.InnerException.GetType() == typeof(TException)) | |
{ | |
ex = tiex.InnerException as TException; | |
} | |
} | |
if (ex == null) | |
{ | |
throw new AssertException( | |
String.Format( | |
"Expected exception {0} from ctor of {1} for param {2} in use case {3}", | |
typeof(TException).Name, | |
typeof(T).Name, | |
arg, | |
useCase) | |
); | |
} | |
if (auxiliaryAssertion != null) | |
{ | |
if (auxiliaryAssertion(ex) == false) | |
{ | |
throw new AssertException( | |
String.Format( | |
"Expected exception {0} from ctor of {1} for param {2} in use case {3} occured but auxiliary assertion failed", | |
typeof(TException).Name, | |
typeof(T).Name, | |
arg, | |
useCase) | |
); | |
} | |
} | |
return ex; | |
} | |
private static IEnumerable<object> BuildArguments( | |
Type type, | |
IEnumerable<ParameterInfo> args) | |
{ | |
return args.Select(GetArgValue); | |
} | |
private static object GetArgValue(ParameterInfo x) | |
{ | |
if (x.ParameterType == typeof(string)) | |
{ | |
return Guid.NewGuid().ToString(); | |
} | |
if (x.ParameterType.IsPrimitive) | |
{ | |
return GetDefault(x.ParameterType); | |
} | |
var mostComplexCtor = x.ParameterType | |
.GetConstructors() | |
.OrderByDescending(c => c.GetParameters().Length) | |
.First(); | |
var ctorArguments = BuildArguments( | |
x.ParameterType, | |
mostComplexCtor.GetParameters()) | |
.ToArray(); | |
object argValue = null; | |
try | |
{ | |
argValue = mostComplexCtor.Invoke(ctorArguments); | |
} | |
catch (Exception ex) | |
{ | |
throw new InvalidOperationException( | |
String.Format("Error invoking ctor for {0}", x.Name), | |
ex); | |
} | |
return argValue; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment