如何使用AutoFixture在保留类型自定义的同时使用自定义特性进行构建

本文关键字:自定义 构建 AutoFixture 何使用 保留 类型 | 更新日期: 2023-09-27 17:59:27

我正在尝试使用自动固定来创建对象,但有些属性我希望始终是默认的(而其他属性可以自动生成)。但是,每当我设置自定义项时,当我使用自定义项构建时,它就会被覆盖。

void Main()
{
    var fixture = new Fixture();
    fixture.Customize<Person>(composer => composer.With(p => p.Name, "Ben"));
    var person = fixture.Build<Person>()
        .With(p => p.DateOfBirth, new DateTime(1900, 1, 1))
        .Create();
    /*  RESULT OF person below
    Name    null
    DateOfBirth 1/1/1900
    StreetAddress   StreetAddressafd6b86b-376a-4355-9a9c-fbae34731453
    State   State019e867b-ac5e-418f-805b-a64146bc06bc
    */
}
public class Person
{
    public string Name { get; set;}
    public DateTime DateOfBirth { get; set;}
    public string StreetAddress { get; set;}
    public string State { get; set;}
}

NameDateOfBirth属性自定义不冲突,所以我不知道为什么Name最终为null。我希望名字是Ben

如何获得它以便应用两个自定义项(即Name = "Ben"DateOfBirth = 1/1/1900)?

如何使用AutoFixture在保留类型自定义的同时使用自定义特性进行构建

正如@DavidOsborne正确指出的,您看到的行为是按设计的。

更好的方法是将您的自定义组织在单独的类中,然后根据特定测试场景的需要启用它们。

自定义对象实现ICustomization接口,其工作是以特定方式配置Fixture对象。这里有一个例子:

public class AllPersonsAreNamedBen : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Person>(composer =>
            composer.With(p => p.Name, "Ben"));
    }
}
public class AllPersonsAreBornIn1900 : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Person>(composer =>
            composer.With(p => p.DateOfBirth, new DateTime(1900, 1, 1)));
    }
}

您可以使用Customize方法在特定的Fixture上启用自定义,例如:

fixture.Customize(new AllPersonsAreNamedBen());

或:

fixture.Customize(new AllPersonsAreBornIn1900());

您还可以使用CompositeCustomization类将多个自定义项组合为一个新的自定义项:

public class AllPersonsAreNamedBenAndAreBornIn1900 : CompositeCustomization
{
    public AllPersonsAreNamedBenAndAreBornIn1900()
        : base(new AllPersonsAreNamedBen(),
               new AllPersonsAreBornIn1900())
    {
    }
}

这时你可以简单地说:

fixture.Customize(new AllPersonsAreNamedBenAndAreBornIn1900());

然而,请记住,在Fixture上应用自定义的顺序很重要:正如@MarkSeemann在评论中指出的那样,最后一个获胜,并且可能覆盖前一个。这也是设计出来的。

因此,虽然可以组合适用于不同类型的现有自定义,但在这种特殊情况下,由于两个自定义都针对同一类型,因此必须创建一个新的自定义来封装组合的Person类型的所有设置:

public class AllPersonsAreNamedBenAndAreBornIn1900 : CompositeCustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Person>(composer =>
            composer.With(p => p.Name, "Ben")
                    .With(p => p.DateOfBirth, new DateTime(1900, 1, 1)));
    }
}

一般来说,保持您的自定义较小且以为中心使您能够在不同的测试中重用它们,并将它们组合用于特定的测试场景。

这看起来像是设计的:

请注意,构建方法链最好理解为一次性定制。它绕过Fixture实例上的所有自定义设置。相反,它允许在构建特定样本时进行细粒度控制。然而,在大多数情况下,添加基于约定的ICustomization是一个更好、更灵活的选项。

来自CCD_ 15方法的文档。

我很感激这可能不是一个理想的答案。然而,文档确实提供了一个提示,说明您可能会如何获得一个。