【问题标题】:Omit property of base class for all implementations in Autofixture省略 Autofixture 中所有实现的基类属性
【发布时间】:2018-04-05 19:03:28
【问题描述】:

我的课程如下:

public class BaseItem
{
    public Guid Id { get; set; }

    public Language Language { get; set; }
}

public class ItemA : BaseItem
{
    public string Name { get; set; }
}

public class ItemB : BaseItem
{
    public string Path { get; set; }
}

我想在每次创建 ItemAItemBBaseItem 类的任何未来实现时省略属性 Language

我在自定义类中添加了这一行(如下),但在生成ItemA/ItemB 时,AutoFixture 仍然尝试生成Language 属性。

fixture.Register<BaseItem>(() => { return fixture.Build<BaseItem>().Without(i => i.Language).Create(); });

像这样(见下文)在实现类级别上省略该属性是可行的。

fixture.Register<ItemA>(() => { return fixture.Build<ItemA>().Without(i => i.Language).Create(); });

我的问题是:

  1. 如何实现一个通用的方法来做到这一点,这样我就不必自定义每个实现类?
  2. 如果有办法执行 #1,如果我希望为某些异常生成 Language 属性,我该如何覆盖它?

【问题讨论】:

    标签: autofixture


    【解决方案1】:

    第一季度

    为了定位(或者更确切地说,省略)在基类上定义的属性,我知道没有比定义自定义 ISpecimenBuilder 更好的方法了,在这种情况下类似于:

    public class OmitLanguageBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var pi = request as PropertyInfo;
            if (pi != null &&
                pi.PropertyType == typeof(Language)
                && pi.Name == "Language"
                && pi.DeclaringType == typeof(BaseItem))
                return new OmitSpecimen();
    
            return new NoSpecimen();
        }
    }
    

    这专门处理仅在BaseItem 上声明的Language 属性的请求。如果您想不那么具体,您可以删除if 表达式中的一些谓词。当 AutoFixture 收到这样的请求时,它会将特殊的“信号类型”OmitSpecimen 解释为忽略该属性的指令。

    您可以直接将OmitLanguageBuilder 注册到Fixture 实例(尽管您应该将其打包在自定义中。此测试通过:

    [Fact]
    public void Q1A()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(new OmitLanguageBuilder());
    
        var actual = fixture.Create<ItemA>();
    
        Assert.Null(actual.Language);
        Assert.NotEqual(default(Guid), actual.Id);
        Assert.NotNull(actual.Name);
    }
    

    ItemB 的类似测试也通过了。

    第二季度

    当您想在特殊测试用例中填充 Language 时,最简单的解决方案可能就是在事后填充它:

    [Fact]
    public void Q2SetAfter()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(new OmitLanguageBuilder());
    
        var actual = fixture.Create<ItemA>();
        actual.Language = fixture.Create<Language>();
    
        Assert.NotNull(actual.Language);
        Assert.NotEqual(default(Guid), actual.Id);
        Assert.NotNull(actual.Name);
    }
    

    另一种选择是使用Build API:

    [Fact]
    public void Q2Build()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(new OmitLanguageBuilder());
    
        var actual = 
            fixture.Build<ItemA>().With(x => x.Language, fixture.Create<Language>()).Create();
    
        Assert.NotNull(actual.Language);
        Assert.NotEqual(default(Guid), actual.Id);
        Assert.NotNull(actual.Name);
    }
    

    最后,如果您需要更改 AutoFixture 处理类型的一般方式,您可以执行以下操作:

    [Fact]
    public void Q2Customize()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(new OmitLanguageBuilder());
        fixture.Customize<ItemA>(c => c.With(x => x.Language, fixture.Create<Language>()));
    
        var actual = fixture.Create<ItemA>();
    
        Assert.NotNull(actual.Language);
        Assert.NotEqual(default(Guid), actual.Id);
        Assert.NotNull(actual.Name);
    }
    

    【讨论】:

    • Q1 就像一个魅力 :) 我已经以通用的方式实现了它作为 PropertyTypeOmitter。关于Q2,我想知道当使用自定义属性创建夹具时,是否有办法在测试方法中访问夹具?所以以 [Theory][CustomAutoData] 为例
    • @AdeliaBernalius 您可以将IFixture 参数添加到您的测试方法中。
    • 太棒了!不知道我能做到这一点:D 谢谢马克!
    猜你喜欢
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-06
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 1970-01-01
    相关资源
    最近更新 更多