我使用反射来填充给定的c#类与测试数据
public object CreateObj(Type type)
{
var obj = Activator.CreateInstance(type);
var fields = type.GetFields();
foreach (var field in fields)
{
field.SetValue(obj, GetRnd(field.FieldType));
}
return obj;
}
GetRnd()
函数根据字段类型设置值:
private object GetRnd(Type type)
{
if (type == typeof(int))
{
return 4;
}
else if (type == typeof(string))
{
return "text";
}
else
{
throw new Exception();
}
}
这工作只要我传递给CreateObj()
一个适当的类。我想让它工作,甚至与基本类型(字符串,int等…)到目前为止,当我传递一个简单的"int"时,我得到了一个"SetType(): Cannot set a constant field"异常。
首先:对于单元测试,不建议使用随机值创建测试对象。
单元测试的目的是在测试的类中找到错误。当前版本和未来版本中的错误。创建单元类不是为了查找使用正常输入时发生的错误。在代码更改后第一次运行程序时已经发现了这些错误。其主要目的是查找在正常运行程序时不会发现的错误。
你的单元测试只有在极少数情况下才能发现错误,才能给你的代码带来信心。
通常它们被称为边缘条件:最低值,最高值,空值,最大值,负值,空值等。
创建一个充满随机值的类是在创建创建测试对象的代码上投入了大量的精力,该测试对象可能会发现与您可以在一分钟内创建的任何非边缘测试对象相同的错误。在使用空数组、负数元素或偶数主数等测试代码之前,您必须运行测试一百万次。
因此,不要花任何精力创建一个带有随机值输入的单元测试。
只有一些正常输入的情况,并尝试找到大量具有边缘条件的测试。
但是,创建一个工厂来创建填充随机值的任意类型的类可能是一个有趣的练习。
首先你应该只初始化那些可写的属性,因此你应该检查它们是否可写。使用System.PropertyInfo.IsWritable。
此外,扩展您的GetRnd函数,使其初始化任何基本类型使用System.Type.IsPrimitive和System.Type.GetTypeCode()
如果你的一个可写属性是一个类,你的类将失败。在这种情况下,您可以递归地初始化该类。使用System.Type.IsClass
你还想用非默认构造函数初始化类吗?使用System.Type.GetConstructors来查看它是否存在,并检查是否有其他可用的构造函数。
数组呢?
额外的困难:如果你的类有一个只读属性,你可以改变返回值。
class RandomObjectFactory
{
// creates an object of type T that has a default constructor
public T CreateObject<T>() where T: class, new()
{
...
此方法优先于带有Type参数的方法,因为使用此方法,如果您尝试编写
,编译器会报错:MyClass m = CreateObject<YourClass>();
此外,如果MyClass没有默认构造函数,编译器会报错。
同样,为基本类型创建一个方法也是明智的:
public T CreatePrimitive<T> where T: struct, IConvertible
{
...
这将防止如下错误:
int i = Create(typeof(Form));
创建对象的代码:
public T CreateObject<T>() where T: class, new()
{
var obj = Activator.CreateInstance<T>();
foreach (var property in typeof(T).GetProperties()
.Where(property => property.CanWrite))
{
if (property.PropertyType.IsPrimitive)
{
property.SetValue(obj, this.CreatePrimitive
(Type.GetTypeCode(property.PropertyType)));
}
else if (property.PropertyType.IsClass)
{
property.SetValue(obj, this.CreatObject...
这里有问题:我们不能传递property.PropertyType.
要解决这个问题:创建一个私有函数CreateObject,它接受一个system。键入并返回一个对象。因为它是私有的,所以没有人可以错误地使用它。private object CreateObject(Type type)
{
var obj = Activator.CreateInstance(type);
foreach (var property in typeof(T).GetProperties()
.Where(property => property.CanWrite))
{
if (property.PropertyType.IsPrimitive)
{
property.SetValue(obj, this.CreatePrimitive
(Type.GetTypeCode(property.PropertyType)));
}
else if (property.PropertyType.IsClass)
{
property.SetValue(obj, this.CreateObject (property.PropertyType);
}
}
return obj;
}
private object CreatePrimitive(TypeCode typeCode)
{
switch (typeCode)
{
case TypeCode:Boolean:
return this.rnd.Next(2) == 0;
case TypeCode.Byte:
return (Byte)this.rnd.Next(Byte.MinValue, Byte.MaxValue);
case TypeCode.DateTime:
long ticks = (long)((DateTime.MaxValue.Ticks - DateTime.MinValue.Ticks) * rnd.NextDouble() + DateTime.MinValue.Ticks);
return new DateTime(ticks);
// etc.
}
return obj;
}
创建一个类似的结构体或数组
检查类型是否为值' type '。
你没有问问题,但我认为你想知道的是:
如何更改此代码以设置常量或只读字段?
你不能。只读。
你可以通过检查只读或常量字段来防止错误:我怎么能得到这个工作与
int
和其他类型
foreach (var field in fields)
{
if (!(field.IsInitOnly || field.IsLiteral))
field.SetValue(obj, GetRnd(field.FieldType));
}
我可以使用这个类来生成一个随机整数或字符串吗?
不是通过改变它的字段。像int
和string
这样的基本类型是不可变的。您可以"更改"变量值的唯一方法是用新值覆盖它。所以你需要处理不可变类型的情况:
static Random rand = new Random();
public object CreateObj(Type type)
{
if(type == typeof(int)) return GetRnd(type);
if(type == typeof(string)) return GetRnd(type);
var obj = Activator.CreateInstance(type);
var fields = type.GetFields();
foreach (var field in fields)
{
field.SetValue(obj, GetRnd(field.FieldType));
}
return obj;
}
还有什么可以改进的?
- 它只设置字段,不设置属性。如果你有一个类的属性包装私有字段,那么他们不会设置
- 对于没有公共无参数构造函数的类型不起作用