In our .NET tests we use NSubstitute
& ExpectedObjects
.
Testing object expectations involves hand crafting large anonymous objects and when new properties are added we need to go back to these anonymous objects & update them.
Attempting below to get a fluent Object to DTO builder - which fails when a property is missed.
Here is the implementation:
//CustomerCreatedEvent has all below properties
var exp1 = @event.ToDto<CustomerCreatedEvent,CustomerDetail>(
x=> x.AggregateId.As("CustomerId"),
x => x.Email, // comment this out and test FAILS -> CustomerDetail.Email required
x => x.FirstName,
x => x.Surname);
var exp2 = new {
CustomerId= @event.AggregateId,
@event.Email, // comment this out and test still passes
@event.FirstName,
@event.Surname};
exp1.ToExpectedObject().ShouldMatch(actual);
exp2.ToExpectedObject().ShouldMatch(actual);
I have 2 questions:
- Is my code just adding 'noise'?
Is the implementation code below sound?
public static TResult ToDto(this TSource obj, params Expression>[] items) where TSource : class { var eo = new ExpandoObject(); var props = eo as IDictionary;
foreach (var item in items) { var member = item.Body as MemberExpression; var unary = item.Body as UnaryExpression; var body = member ?? (unary != null ? unary.Operand as MemberExpression : null); if (member != null && body.Member is PropertyInfo) { var property = body.Member as PropertyInfo; props[property.Name] = obj.GetType() .GetProperty(property.Name) .GetValue(obj, null); } else { var property = unary.Operand as MemberExpression; if (property != null) { props[property.Member.Name] = obj.GetType() .GetProperty(property.Member.Name) .GetValue(obj, null); } else { var compiled = item.Compile(); var output = (KeyValuePair<string, object>)compiled.Invoke(obj); props[output.Key] = obj.GetType() .GetProperty(output.Value.ToString()) .GetValue(obj, null); } } } TResult result = Activator.CreateInstance<TResult>(); foreach (var item in props) { result.GetType().GetProperty(item.Key).SetValue(result, item.Value, null); } return result; }
}