我正在尝试学习AutoFixture,并且我已经通过xUnit和NSubstitute以及AutoFixture设置来自动模拟伪造的属性(使用AutoFixture.AutoNSubstitute)。换句话说,如果我有以下界面
public interface IFoo
{
IBar1 Bar1 {get;}
IBar2 Bar2 {get; set;}
}
尝试解析IFoo将自动解析并填充Bar1和Bar2。
对于具有接口,具体对象或结构类型属性的对象,一切都很有效。我遇到了使AutoFixture自动创建抽象类型属性的问题。
我尝试使用TypeRelay作为抽象类型,所以
fixture.Customizations.Add(new TypeRelay(typeof (AbstractBase), typeof (ConcreteChild)));
我试过这样指定,
fixture.Customize<AbstractBase>(
composer =>
composer.FromFactory(
(string ChildParam1, string ChildParam2) => new ConcreteChild(ConcreteChildParam1, ConcreteChildParam2)));
我尝试过使用各种自定义标本制作工具
通过属性类型解析:
var pi = request as PropertyInfo;
if (pi != null &&
pi.PropertyType == typeof(AbstractBase))
return context.Resolve(typeof(ConcreteChild));
return new NoSpecimen(request);
通过班级类型解决:
var pi = request as Type;
if (pi != null &&
pi == typeof (AbstractBase))
return context.Resolve(typeof(ConcreteChild));
return new NoSpecimen(request);
使用上述两种解决方案,我也尝试了context.Create<ConcreteChild>()
最后,我尝试使用Register<AbstractBase>(fixture.Create<ConcreteChild>);
语法。
它们似乎都无法在对象上自动填充属性。
令人恼火的是,我可以在单独的测试中明确地fixture.Create<AbstractBase>();
并获得ConcreteChild
,然后手动阻塞所有东西,但这种方式会破坏AutoFixture no的目的吗?
有什么想法吗?
更新
抽象类。我已经修剪了大部分不可靠的东西,留下了代码,因为我假设它被调用了?
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
是一个相当标准的枚举。
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;
}
}
答案 0 :(得分:2)
有点尴尬,我意识到NSubstitute有它所谓的recursive mocking,这部分是我想要的,并解释了为什么我无法弄清楚一些自动模拟属性的来源。问题在于它并没有全面实现(可能是正确的),就我所知,在这方面并不是真正可扩展的。
现在AutoFixture开始发挥作用,在我们在后处理器NSubstituteBuilder
中创建样本后,调用AutoPropertiesCommand
类并获取它所确定的用于填充数据的适当属性。
不幸的是,两个相关类中的逻辑(存在来自泛型AutoPropertiesCommand
类的非泛型继承)都不可覆盖,这就是问题发生的地方。具体来说,AutoPropertiesCommand<T>
有两个用于获取属性和字段的方法,然后使用提供的IRequestSpecification
(在这种情况下为PropertiesOnlySpecification
)进行过滤。
有问题的方法
private IEnumerable<PropertyInfo> GetProperties(object specimen)
{
return from pi in this.GetSpecimenType(specimen).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where pi.GetSetMethod() != null
&& pi.GetIndexParameters().Length == 0
&& this.specification.IsSatisfiedBy(pi)
select pi;
}
所以这里的解决方案是在没有上述限制的情况下提供我自己的AutoPropertiesCommand
实现,或者为我遇到的每个案例显式创建自定义。还没有决定我更喜欢哪种方法,但可能是前者。
作为一个侧边栏,似乎没有将这两种方法作为受保护的虚拟方式进行限制,是否有任何特殊原因超出“它只是以这种方式编码”?这些是我认为的基础AutoFixture类,用于记录。