抽象属性上的AutoMocking属性失败?

时间:2014-06-26 16:36:59

标签: c# xunit.net autofixture nsubstitute

我正在尝试学习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;
        }
    }

1 个答案:

答案 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类,用于记录。