I'm trying to learn AutoFixture, and I've got xUnit and NSubstitute and AutoFixture setup to automatically mock out properties with fakes (with AutoFixture.AutoNSubstitute). In other words, if I have the following interface
public interface IFoo
{
IBar1 Bar1 {get;}
IBar2 Bar2 {get; set;}
}
Trying to resolve an IFoo will automatically resolve and populate Bar1 and Bar2.
Everything works great for objects with properties of interface, concrete object, or structure types. I'm having a problem getting AutoFixture to automatically create properties of abstract types however.
I have tried using a TypeRelay for the abstract type, so
fixture.Customizations.Add(new TypeRelay(typeof (AbstractBase), typeof (ConcreteChild)));
I have tried specifying it this way,
fixture.Customize<AbstractBase>(
composer =>
composer.FromFactory(
(string ChildParam1, string ChildParam2) => new ConcreteChild(ConcreteChildParam1, ConcreteChildParam2)));
I have tried using various custom specimen builders
Resolving via the property type:
var pi = request as PropertyInfo;
if (pi != null &&
pi.PropertyType == typeof(AbstractBase))
return context.Resolve(typeof(ConcreteChild));
return new NoSpecimen(request);
Resolving via the class type:
var pi = request as Type;
if (pi != null &&
pi == typeof (AbstractBase))
return context.Resolve(typeof(ConcreteChild));
return new NoSpecimen(request);
With both of the above solutions, I also tried context.Create<ConcreteChild>()
Finally I have tried using the Register<AbstractBase>(fixture.Create<ConcreteChild>);
syntax.
None of them seem to work as far as automatically populating properties on an object.
The irritating thing is that I can explicitly fixture.Create<AbstractBase>();
in an individual test and get the ConcreteChild
and then hand-jam everything but that kind of defeats the purpose of AutoFixture no?
Any ideas?
UPDATE:
The abstract class. I've pruned most of the irrelivent stuff, left the ctor code in as I'm assuming it gets called?
public abstract class ChatEntityId
{
private string _localName;
protected ChatEntityId(string chatRoomName, string entityUid, ChatProtocol protocol)
{
ErrorChecker.NormalizeToNullIfNotSet(ref chatRoomName);
ErrorChecker.NormalizeToNullIfNotSet(ref entityUid);
if (chatRoomName == null && entityUid == null)
{
throw new ArgumentException("Both chatRoomName and entityUid may not be null at the same time.");
}
ChatRoomName = chatRoomName;
EntityUid = entityUid;
Protocol = protocol;
}
public string ChatRoomName { get; private set; }
public string EntityUid { get; private set; }
public bool Equals(ChatEntityId chatEntityId) { }
public override bool Equals(object obj) { }
public override int GetHashCode() {}
public string LocalName { get; }
public ChatProtocol Protocol { get; private set; }
public override string ToString() { }
}
ChatProtocol
is an enum, fairly standard.
The AutoPopulatedProperty ICustomization
public virtual void Customize(IFixture fixture)
{
fixture.Customize(new DomainCustomization());
// Replacement for the AutoNSubstituteCustomization, this Postprocessor will automatically create fake objects on properties.
fixture.ResidueCollectors.Add(
new Postprocessor(
new NSubstituteBuilder(
new MethodInvoker(
new NSubstituteMethodQuery())),
new AutoPropertiesCommand(
new PropertiesOnlySpecification())));
}
private class PropertiesOnlySpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
return request is PropertyInfo;
}
}