【问题标题】:AutoMocking Properties Fails on Abstract Property?AutoMocking 属性在抽象属性上失败?
【发布时间】:2014-06-26 16:36:58
【问题描述】:

我正在尝试学习 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&lt;ConcreteChild&gt;()

最后我尝试使用Register&lt;AbstractBase&gt;(fixture.Create&lt;ConcreteChild&gt;); 语法。

它们似乎都不能自动填充对象的属性。

令人恼火的是,我可以在一个单独的测试中明确地fixture.Create&lt;AbstractBase&gt;(); 并获得ConcreteChild,然后手动卡住所有东西,但这会违背 AutoFixture 的目的吗?

有什么想法吗?

更新:

抽象类。我已经修剪了大部分无关紧要的东西,留下了我假设它被调用的 ctor 代码?

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;
        }
    }

【问题讨论】:

  • 能否提供抽象类型的代码?
  • 已更新。我不一定确定我是否清楚地解释了实际问题(我不确定),我昨天有一个想法/实现,我认为 AutoFixture 的工作方式可能是一个错误的假设。我假设用于自动填充假对象的后处理器会返回并从夹具发出请求,但我不确定它是否正在这样做?我已经添加了我从其他地方提取的代码,因为它具有 AutoNSubstitute 自动模拟属性。
  • this gist 是否重现您的案例?
  • 如果这个要点重现了这个案例,它看起来像是 AutoFixture 和 NSubstitute 之间交互的限制。它可能类似于this AutoMoq behaviour,但我不知道属性的默认 NSubstitute 行为是什么......
  • 是的,完全正确。顺便说一句,我对 NSubstitute 的默认行为也不太了解。 OOTB,目前只有 AutoFixture.AutoFakeItEasy 通过了两个测试。

标签: c# xunit.net autofixture nsubstitute


【解决方案1】:

有点尴尬的是,我意识到 NSubstitute 有它所谓的 recursive mocking,这部分是我想要的,并解释了为什么我无法弄清楚一些自动模拟属性的来源。问题是它并没有全面做到这一点(可能是正确的),据我所知,在这方面并没有真正的可扩展性。

现在 AutoFixture 开始发挥作用,在我们在 Postprocessors NSubstituteBuilder 中创建样本后,AutoPropertiesCommand 类被调用并获取它所确定的适当属性以填充数据。

不幸的是,两个相关类(有一个从泛型AutoPropertiesCommand 类继承的非泛型)中的逻辑都不是可覆盖的,这就是问题发生的地方。具体来说,AutoPropertiesCommand&lt;T&gt; 有两种方法用于获取属性和字段,然后使用提供的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 类,记录在案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-12
    • 2011-01-31
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    相关资源
    最近更新 更多