Create a gist now

Instantly share code, notes, and snippets.

anonymous /ReflectionHelper.cs
Created Dec 22, 2011

TestCollectionHelper
public static class ReflectionHelper
{
public static MemberExpression GetMemberExpressionGeneric<TModel, T>(Expression<Func<TModel, T>> expression)
{
return GetMemberExpression(expression, true);
}
public static MemberExpression GetMemberExpressionGeneric<TModel, T>(Expression<Func<TModel, T>> expression, bool enforceCheck)
{
return GetMemberExpression(expression, enforceCheck);
}
public static MemberExpression GetMemberExpression(LambdaExpression expression, bool enforceCheck)
{
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
if (enforceCheck && memberExpression == null)
{
throw new ArgumentException(@"Not a member access", "expression");
}
return memberExpression;
}
public static string GetPropertyNameFrom<T>(Expression<Func<T, object>> property)
{
var member = GetMemberExpressionGeneric(property);
return member.Member.Name;
}
}
public class CollectionTestHelper<T>
{
private IEnumerable<T> _list;
private Dictionary<Expression<Func<T, object>>, object> _missMatches;
private IEnumerable<T> _unfiltered;
private Dictionary<Expression<Func<T, object>>, object> _filters;
public CollectionTestHelper(IEnumerable<T> list)
{
_list = list;
_unfiltered = list;
_missMatches = new Dictionary<Expression<Func<T, object>>, object>();
_filters = new Dictionary<Expression<Func<T, object>>, object>();
}
public CollectionTestHelper<T> And<T2>(Expression<Func<T, T2>> filter, T2 value)
{
var untypedFilter = AddBox(filter);
_filters.Add(untypedFilter, value);
var filterFunc = filter.Compile();
_list = _list.Where(x => Equals(filterFunc(x),value)).ToList();
if (_list.Count() == 0)
{
_list = _unfiltered;
_missMatches.Add(untypedFilter, value);
}
_unfiltered = _list;
return this;
}
public T Single()
{
var typeName = typeof (T).Name;
if (_missMatches.Count > 0)
{
var message = new StringBuilder();
message.AppendFormat("{0} not found, mismatch on ", typeName);
foreach (var key in _missMatches.Keys)
{
message.AppendFormat("{0}: {1}, ", ReflectionHelper.GetPropertyNameFrom(key), _missMatches[key]);
}
message.AppendFormat("\nFound with:\n");
foreach (var item in _list)
{
message.Append("\t");
foreach (var key in _missMatches.Keys)
{
message.AppendFormat("{0} = {1}, ", ReflectionHelper.GetPropertyNameFrom(key), key.Compile()(item));
}
message.Append("\n");
}
Assert.Fail(message.ToString());
}
if (_list.Count() > 1)
{
MoreThanOneMatchFail(typeName);
}
return _list.Single();
}
private void MoreThanOneMatchFail(string typeName)
{
var filters = new StringBuilder();
foreach (var propertyExpression in _filters.Keys)
{
var propertyName = ReflectionHelper.GetPropertyNameFrom(propertyExpression);
var value = _filters[propertyExpression];
filters.AppendFormat("{0}: {1}, ", propertyName, value);
}
Assert.Fail("More than one {0} was found for defined filters {1}", typeName, filters);
}
private Expression<Func<TInput, object>> AddBox<TInput, TOutput>(Expression<Func<TInput, TOutput>> expression)
{
// Add the boxing operation, but get a weakly typed expression
Expression converted = Expression.Convert(expression.Body, typeof(object));
// Use Expression.Lambda to get back to strong typing
return Expression.Lambda<Func<TInput, object>>(converted, expression.Parameters);
}
}
public static class CollectionTestExtensions
{
public static CollectionTestHelper<T> AssertContains<T, T2>(this IEnumerable<T> list, Expression<Func<T, T2>> filterProperty, T2 value)
{
return new CollectionTestHelper<T>(list).And(filterProperty, value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment