问题
Does AutoFixture have a facility to create multiple instances of a given type with all the same data? My classes are not serializable and I need two models which are not reference equivalent, but instead have matching properties.
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);
回答1:
I don't think that AutoFixture can do this, but Albedo can. While this is only proof-of-concept code, it should illustrate the general idea, I hope.
Create a new class, deriving from 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; }
}
}
The pivotal part of the implementation is the Visit
overload where it uses Reflection to copy each property from source
to destination
object.
Since this implementation mutates destination
, the Value
property is never used, but it has to be there because it's abstract
in ReflectionVisitor<T>
...
You can now write your test as:
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);
Here, I still used fixture
to create b
, but you don't have to do that, because all properties are going to be overwritten anyway. If Foo
has a parameterless constructor, you could instead simply use new Foo()
; it'd make no difference.
This proof-of-concept explicitly only copies properties. If you need to copy fields as well, you'll have to override the appropriate Visit
method as well. Furthermore, if the objects in question take constructor arguments, you'll need to deal with those explicitly as well.
If you find it tedious to write new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));
, I'm sure you can figure out a way to write a helper method around it.
As an additional note, PropertyCopyVisitor
doesn't have to be generic, because it doesn't actually use the T
type argument for anything. I just like the type safety that this gives the constructor...
来源:https://stackoverflow.com/questions/47165015/generating-complex-types-with-all-properties-equivalent