#c# #reflection
#c# #отражение
Вопрос:
У меня есть два объекта, которые имеют в основном свойства с одинаковым типом и именем, и у меня есть метод, который присваивает значения из объекта A соответствующим свойствам в объекте B. Я пишу модульный тест, который должен вызывать этот метод, а затем утверждать, что для каждого свойства в объекте A, которое имеет соответствующее свойство в объекте B, значение было скопировано.
Моя идея для достижения этой цели состоит в том, чтобы использовать отражение для перечисления всех свойств в объекте A, а затем присвоить каждому свойству случайное значение. Затем я вызываю свой метод «копировать значения», чтобы скопировать значения в объект B, а затем использую отражение, чтобы перечислить поля для каждого объекта и убедиться, что поля с совпадающими именами имеют одинаковое значение.
Вот мой код для присвоения случайных значений объекту A
var objectA = new ObjectA();
var random = new Random();
var fields = typeof(ObjectA).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
if (field.CanWrite)
{
if (field.PropertyType == typeof(bool) || field.PropertyType == typeof(bool?))
{
field.SetValue(objectA, Convert.ToBoolean(random.Next(2)));
}
if (field.PropertyType == typeof(decimal) || field.PropertyType == typeof(decimal?))
{
field.SetValue(objectA, Convert.ToDecimal(random.Next()));
}
if (field.PropertyType == typeof(string))
{
field.SetValue(objectA, random.Next().ToString());
}
// similar code for other data types
}
}
Затем я вызываю свой метод для копирования значений:
ObjectB objectB = ObjectB.FromObjectA(objectA);
Затем я вызываю этот метод для сравнения значений двух объектов:
public static void AssertMatchingFieldAreEqual<T1, T2>(T1 a, T2 b)
{
foreach (var fieldA in typeof(T1).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var fieldInfoB = typeof(T2).GetProperty(fieldA.Name);
if (fieldInfoB != null)
{
var propertyA = typeof(T1).GetProperty(fieldA.Name);
var propertyB = typeof(T2).GetProperty(fieldA.Name);
// Adding field names to make error messages for failed Assert calls list the field name
var valueA = $"{propertyA.Name}: {propertyA.GetValue(a)}";
var valueB = $"{propertyB.Name}: {propertyB.GetValue(b)}";
Assert.AreEqual(valueA, valueB);
}
}
}
Это работает для базовых типов данных. Моя проблема в том, что у меня есть несколько полей, которые равны List
s, и я хотел бы заполнить их случайным количеством объектов их типа, а затем утверждать, что при копировании полей в каждом списке одинаковое количество объектов.
Мои два вопроса:
-
Когда я присваиваю значения, как мне проверить, является ли свойство a
List
, не зная типа элемента в списке? Я пробовалif (field.PropertyType == typeof(List<object>)
, но это не работает. -
Как мне создать новый объект типа
T
add и добавить его в мой список, если мой тип свойства — список?
Или, в качестве альтернативы, если есть лучший способ проверить, что мой метод «копировать значения» копирует все поля с одинаковыми именами, какой способ лучше?
Ответ №1:
Прямой ответ на ваш вопрос заключается в том, что вам нужно будет включить общее тестирование в свое отражение.
if(field.PropertyType.GetGenericTypeDefinition() == typeof(List<>)){
var typeOfThingInside = field.PropertyType.GetGenericArguments()[0];
// ...
}
Лучший способ — использовать автофиксацию для создания ваших объектов и использовать Fluent Assertions, чтобы проверить, что все скопировано.
void Main()
{
// Arrange
var fixture = new Fixture();
var a = fixture.Create<ObjectA>();
// Act
var b = ObjectB.FromObjectA(a);
// Assert
b.Should().BeEquivalentTo(a, options => options.ExcludingMissingMembers());
}
public class ObjectA {
public int A {get;set;}
public string B {get;set;}
public List<string> C {get;set;}
public decimal Z {get;set;}
}
public class ObjectB
{
public int A { get; set; }
public string B { get; set; }
public List<string> C { get; set; }
public decimal Y { get; set; }
public static ObjectB FromObjectA(ObjectA a) => JsonConvert.DeserializeObject<ObjectB>(JsonConvert.SerializeObject(a));
}
Комментарии:
1. Это здорово, спасибо. Я не знал об автофиксации и плавных утверждениях. Они оба делают это намного проще.