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,
x => x.FirstName,
x => x.Surname);
var exp2 = new {
CustomerId= @event.AggregateId,
@event.Email, // if i comment out this line this test will still pass
@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 T2 ToDto<T1,T2>(this T1 obj, params Expression<Func<T1, dynamic>>[] items) where T1 : class
{
var eo = new ExpandoObject();
var props = eo as IDictionary<String, object>;
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;
if (property != null)
props[property.Name] = obj.GetType().GetProperty(property.Name).GetValue(obj, null);
}
else if (unary != null)
{
var ubody = (UnaryExpression)thing.Body;
var property = ubody.Operand as MemberExpression;
if (property != null)
{
props[property.Member.Name] = obj.GetType()
.GetProperty(property.Member.Name)
.GetValue(obj, null);
}
else // full expression with number funcs
{
var compiled = item.Compile();
var result = (KeyValuePair<string, object>)compiled.Invoke(obj);
props[result.Key] = result.Value;
}
}
}
string json = JsonConvert.SerializeObject(eo); // need json.net
var anon = JsonConvert.DeserializeAnonymousType<object>(json, Activator.CreateInstance<T2>());
return ((JObject)anon).ToObject<T2>();
}