首先,不建议使用随机值创建测试对象进行单元测试。
您的单元测试的目的是查找正在测试的类中的错误。当前版本和未来版本中的错误。您的单元类不是为了查找在使用正常输入时发生的错误。这些错误在代码更改后第一次运行程序时就已经发现了。主要目的是查找在正常运行程序时无法发现的错误。
仅当单元测试在罕见情况下发现错误时,才能对代码产生信心。通常称为边界条件:最低值、最高值、空值、最大数量的值、负值、null 值等。
创建一个充满随机值的类会花费很多精力,而这个测试对象可能会找到与您在一分钟内可以发明的任何非边缘测试对象相同的错误。您必须运行测试一百万次才能使用空数组、负数元素或偶数质数等测试代码。
因此,请不要花费任何精力用随机值输入创建单元测试。
只有少数几种情况下使用正常输入,并尝试找到许多带有边界条件的测试。
但是,创建一个工厂,它将创建任何类型的任何类并填充随机值,可能是一个有趣的练习。
首先,您应该仅初始化可写的属性,因此应检查它们是否可写。使用 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()
{
...
这种方法比带有类型参数的方法更受欢迎,因为使用这种方法时,如果您尝试编写以下内容,编译器会发出警告:
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);
}
return obj;
}
创建一个类似的东西来创建结构体或数组。