用测试数据填写一个类

时间:2015-08-04 13:28:30

标签: c# unit-testing reflection

我使用反射用测试数据填充给定的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()一个合适的班级,这就有效。即使使用基本类型(字符串,整数等等),我也希望它能够正常工作。 到现在为止,我得到一个" SetType():无法设置常量字段"当我传递一个简单的" int"。

时出现异常

3 个答案:

答案 0 :(得分:2)

  

首先:对于单元测试,建议不要使用随机值创建测试对象。<​​/ p>

单元测试的目的是在您正在测试的类中查找错误。当前版本和未来版本中的错误。未创建单元类以查找使用普通输入时发生的错误。在代码更改后第一次运行程序时,已经发现了这些错误。主要目的是找到在正常运行程序时无法找到的错误。

  

如果在极少数情况下会发现错误,那么您的单元测试只能让您对代码充满信心。

通常它们被称为边缘条件:最低值,最高值,空值,最大值数,负值,空值等。

创建一个充满随机值的类正在花费大量精力创建一个代码,该代码创建一个测试对象,该代码可能会找到与您在一分钟内可以发明的任何非边缘测试对象相同的错误。在使用空数组,负数元素或偶数主数字等测试代码之前,您必须运行测试一百万次。

  

因此,不要在创建具有随机值输入的单元测试时付出任何努力。

只有少数情况下输入正常,并尝试使用边缘条件找到大量测试。

但是,创建一个可以创建任何填充了随机值的类的工厂可能是一个有趣的练习。

首先,您应该只初始化可写的属性,因此您应该检查它们是否可写。使用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.Type并返回一个对象。因为这个是私人的,没有人可以错误地使用它。

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;
}

创建类似于创建结构或数组的东西。

答案 1 :(得分:1)

检查类型是否为值'Type.IsValue',表示类型传入参数是标量类型。

答案 2 :(得分:-1)

你没有提出问题,但这就是我想要知道的

  

如何更改此代码以设置常量或只读字段?

你不能。它们是只读

  

如何使用int和其他类型

嗯,您可以通过检查只读或常量字段来阻止错误

foreach (var field in fields)
{
    if (!(field.IsInitOnly || field.IsLiteral))
        field.SetValue(obj, GetRnd(field.FieldType));
}
  

我可以使用此类生成随机整数或字符串吗?

不要改变它的字段。像intstring这样的原始类型是不可变的。你能改变的唯一方法是#34;变量的值是用新值覆盖它。因此,您需要处理不可变类型的案例:

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;
}
  

如何改进呢?

  • 它只设置字段,而不是属性。如果您的类具有包装私有字段的属性,那么它们将不会被设置
  • 对于没有公共无参数构造函数的类型,它不会工作