BIG阳
我最终选择了一种通用方法,该方法使用反射来自动完成问题显示的手动完成。以这种方式创建的任何字段都可以被覆盖,并且必须手动设置任何复杂类型。布尔值只是设置为真。我很想听听人们对这样做的想法。当我构建单元测试时,它为我节省了大量的时间和精力。这是我的测试最终的外观(简单示例,没有我想用特定值覆盖的复杂类型或字符串/数字字段,尽管这些也被处理):[Test]public void TestCreateCars(){ // Arrange var expectedCars = new List<Car>(); expectedCars.Add(TestUtils.CreateObject<Car>(1)); expectedCars.Add(TestUtils.CreateObject<Car>(2)); expectedCars.Add(TestUtils.CreateObject<Car>(3)); // using moq but anything could be used _carService.Setup(x => x.GetNewCarsInfo()).Returns(cars); var carsFactory = new carFactory(_carService); // Act var cars = carsFactory.CreateCars(); // Assert Assert.AreEqual(3, cars.Count); TestUtils.AssertObjectDefaultFields(cars[0], 1); TestUtils.AssertObjectDefaultFields(cars[1], 2); TestUtils.AssertObjectDefaultFields(cars[2], 3); _carService.VerifyAll();}创建对象的方法:// Use this to instantiate objects of types with a large number of int/string fields for testing. You can override any values after calling this - complex fields won't be set// -sets numeric values on that object to its 'id'// -sets string values on that object to the name of the field concatenated with the 'id'public static T CreateObject<T>(int id){ var instance = (T)Activator.CreateInstance(typeof(T)); // only set properties with a visible setter var properties = typeof(T).GetProperties().Where(prop => prop.GetSetMethod() != null); foreach (var property in properties) { var type = property.PropertyType; if (IsNumeric(type)) { type = Nullable.GetUnderlyingType(type) ?? type; var value = Convert.ChangeType(id, type); property.SetValue(instance, value); } else if (property.PropertyType == typeof(string)) { property.SetValue(instance, property.Name + id); } else if (property.PropertyType == typeof(bool)) { property.SetValue(instance, true); } } return instance;}断言以这种方式创建的对象的方法具有预期值:// Use this to assert that an object created by CreateObject<T> has all of the 'default' values:// -numeric values equal its 'id'// -string values equal the name of the field concatenated with the 'id'// // unsetProperties: property names that we want to assert are their default values// ignoreProperties: property names that we don't want to assert anything against - we should assert against these outside of this methodpublic static void AssertObjectDefaultFields<T>(T obj, int id, HashSet<string> unsetProperties = null, HashSet<string> ignoreProperties = null){ // only test properties with a visible setter, otherwise it wouldnt have been set var properties = typeof(T).GetProperties().Where(prop => prop.GetSetMethod() != null); unsetProperties = unsetProperties ?? new HashSet<String>(); ignoreProperties = ignoreProperties ?? new HashSet<String>(); foreach (var property in properties) { if(!ignoreProperties.Contains(property.Name)) { if (unsetProperties.Contains(property.Name)) { var defaultValue = property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; Assert.AreEqual(defaultValue, property.GetValue(obj)); } else if (IsNumeric(property.PropertyType)) { Assert.AreEqual(id, property.GetValue(obj)); } else if (property.PropertyType == typeof(string)) { Assert.AreEqual(property.Name + id, property.GetValue(obj)); } else if (property.PropertyType == typeof(bool)) { Assert.AreEqual(true, property.GetValue(obj)); } } }}用于测试字段的类型是否为数字:private static bool IsNumeric(Type type){ var NumericTypes = new HashSet<Type> { typeof(int), typeof(double), typeof(decimal), typeof(long), typeof(short), typeof(sbyte), typeof(byte), typeof(ulong), typeof(ushort), typeof(uint), typeof(float) }; return NumericTypes.Contains(Nullable.GetUnderlyingType(type) ?? type);}