Skip to content

Instantly share code, notes, and snippets.

@robertwilczynski
Created May 7, 2013 15:25
Show Gist options
  • Save robertwilczynski/5533492 to your computer and use it in GitHub Desktop.
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
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