生成所有属性都等效的复杂类型
Generating complex types with all properties equivalent
AutoFixture 是否可以使用相同的数据创建给定类型的多个实例?我的 类 不可序列化,我需要两个引用不等效但具有匹配属性的模型。
public class Foo
{
// Many more properties and similar models needing the same semantics.
public string Name { get; set; }
}
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>();
Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);
我不认为 AutoFixture 可以做到这一点,但 Albedo 可以。虽然这只是概念验证代码,但我希望它应该能说明总体思路。
创建一个新的 class,派生自 ReflectionVisitor<T>
:
public class PropertyCopyVisitor<T> : ReflectionVisitor<T>
{
private readonly T source;
private readonly T destination;
public PropertyCopyVisitor(T source, T destination)
{
this.source = source;
this.destination = destination;
}
public override IReflectionVisitor<T> Visit(
PropertyInfoElement propertyInfoElement)
{
var pi = propertyInfoElement.PropertyInfo;
pi.SetValue(this.destination, pi.GetValue(this.source));
return this;
}
public override T Value
{
get { return this.destination; }
}
}
实现的关键部分是 Visit
重载,它使用反射将每个 属性 从 source
复制到 destination
对象。
由于此实现改变了 destination
,因此从未使用 Value
属性,但它必须存在,因为它在 ReflectionVisitor<T>
中是 abstract
。 ..
您现在可以将测试编写为:
var fixture = new Fixture();
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>(); // Or just create a new, empty Foo...
// This copies all properties from a to b:
new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));
Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);
在这里,我仍然使用fixture
创建b
,但您不必这样做,因为无论如何所有属性都会被覆盖。如果 Foo
有一个无参数的构造函数,你可以简单地使用 new Foo()
;没什么区别。
此概念验证明确仅复制属性。如果您还需要复制字段,则还必须覆盖适当的 Visit
方法。此外,如果有问题的对象采用构造函数参数,您也需要显式处理这些参数。
如果你觉得写 new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));
很乏味,我相信你能找到一种方法来围绕它编写一个辅助方法。
作为附加说明,PropertyCopyVisitor
没有 是通用的,因为它实际上并没有将 T
类型参数用于任何东西.我只是喜欢这为构造函数提供的类型安全性...
AutoFixture 是否可以使用相同的数据创建给定类型的多个实例?我的 类 不可序列化,我需要两个引用不等效但具有匹配属性的模型。
public class Foo
{
// Many more properties and similar models needing the same semantics.
public string Name { get; set; }
}
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>();
Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);
我不认为 AutoFixture 可以做到这一点,但 Albedo 可以。虽然这只是概念验证代码,但我希望它应该能说明总体思路。
创建一个新的 class,派生自 ReflectionVisitor<T>
:
public class PropertyCopyVisitor<T> : ReflectionVisitor<T>
{
private readonly T source;
private readonly T destination;
public PropertyCopyVisitor(T source, T destination)
{
this.source = source;
this.destination = destination;
}
public override IReflectionVisitor<T> Visit(
PropertyInfoElement propertyInfoElement)
{
var pi = propertyInfoElement.PropertyInfo;
pi.SetValue(this.destination, pi.GetValue(this.source));
return this;
}
public override T Value
{
get { return this.destination; }
}
}
实现的关键部分是 Visit
重载,它使用反射将每个 属性 从 source
复制到 destination
对象。
由于此实现改变了 destination
,因此从未使用 Value
属性,但它必须存在,因为它在 ReflectionVisitor<T>
中是 abstract
。 ..
您现在可以将测试编写为:
var fixture = new Fixture();
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>(); // Or just create a new, empty Foo...
// This copies all properties from a to b:
new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));
Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);
在这里,我仍然使用fixture
创建b
,但您不必这样做,因为无论如何所有属性都会被覆盖。如果 Foo
有一个无参数的构造函数,你可以简单地使用 new Foo()
;没什么区别。
此概念验证明确仅复制属性。如果您还需要复制字段,则还必须覆盖适当的 Visit
方法。此外,如果有问题的对象采用构造函数参数,您也需要显式处理这些参数。
如果你觉得写 new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));
很乏味,我相信你能找到一种方法来围绕它编写一个辅助方法。
作为附加说明,PropertyCopyVisitor
没有 是通用的,因为它实际上并没有将 T
类型参数用于任何东西.我只是喜欢这为构造函数提供的类型安全性...