如何使用 AutoFixture 在保留类型自定义的同时使用自定义属性进行构建?
How to use AutoFixture to build with customized properties while keeping type customizations?
我正在尝试使用 autofixture 创建一个对象,但我希望某些属性始终为默认属性(而其余属性可以自动生成)。但是,每当我设置自定义项时,它都会在我使用自定义项构建时被覆盖。
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;}
}
Name
和 DateOfBirth
属性 自定义不冲突所以我不知道为什么 Name 最终为空。我希望名字是 Ben
.
我怎样才能得到它以便应用两个自定义项(即 Name = "Ben"
和 DateOfBirth = 1/1/1900
)?
这看起来像是设计使然:
Note that the Build method chain is best understood as a one-off Customization. It bypasses all Customizations on the Fixture
instance. Instead, it allows fine-grained control when building a specific specimen. However, in most cases, adding a convention-based ICustomization
is a better, more flexible option.
...来自 Build()
方法的文档。
我知道这可能不是一个理想的答案。但是,该文档确实提供了有关如何得出结果的提示。
正如@DavidOsborne 正确指出的那样,您看到的行为是 as designed。
A better approach 是在单独的 class 中组织您的自定义,然后根据特定测试场景的需要启用它们。
自定义对象实现 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
class:
将多个自定义组合成一个新的
public class AllPersonsAreNamedBenAndAreBornIn1900 : CompositeCustomization
{
public AllPersonsAreNamedBenAndAreBornIn1900()
: base(new AllPersonsAreNamedBen(),
new AllPersonsAreBornIn1900())
{
}
}
此时你可以简单地说:
fixture.Customize(new AllPersonsAreNamedBenAndAreBornIn1900());
但是,请记住在 Fixture
上应用自定义设置的顺序很重要:最后一个获胜并且可能 覆盖 之前的,正如@MarkSeemann 在评论中指出的那样。这也是by design.
因此,虽然您 可以 组合适用于 不同 类型的现有自定义,但在这种特殊情况下,因为两个自定义都针对相同的类型,您必须创建一个新的自定义项来封装 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)));
}
}
作为一般规则,保持您的自定义小而 集中 使您能够 reuse them 在不同的测试中,将它们组合以用于特定的测试场景。
如已接受的答案所述,自定义将相互覆盖,因此您必须为要使用的每个属性组合编写自定义。
我编写了一个库,允许您编写自定义项,这样它们就不会相互覆盖,而是合并在一起。这将允许您对 People named Ben
进行一项自定义,对 Born in 1900
进行一项自定义,并且您可以将它们与 Compose 函数组合在一起。
它并没有完全解决原始问题,但它可能会给您一些关于如何实现您想要的想法。 Compose 功能是通过创建处理自定义的自定义 IFixture 实现来完成的。
https://github.com/LethargicDeveloper/Lift.AutoFixture
编辑:我看到 "active 4 months ago." 我错过了“3 年”。很抱歉复活这么旧的 post,但希望这会有用。
我正在尝试使用 autofixture 创建一个对象,但我希望某些属性始终为默认属性(而其余属性可以自动生成)。但是,每当我设置自定义项时,它都会在我使用自定义项构建时被覆盖。
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;}
}
Name
和 DateOfBirth
属性 自定义不冲突所以我不知道为什么 Name 最终为空。我希望名字是 Ben
.
我怎样才能得到它以便应用两个自定义项(即 Name = "Ben"
和 DateOfBirth = 1/1/1900
)?
这看起来像是设计使然:
Note that the Build method chain is best understood as a one-off Customization. It bypasses all Customizations on the
Fixture
instance. Instead, it allows fine-grained control when building a specific specimen. However, in most cases, adding a convention-basedICustomization
is a better, more flexible option.
...来自 Build()
方法的文档。
我知道这可能不是一个理想的答案。但是,该文档确实提供了有关如何得出结果的提示。
正如@DavidOsborne 正确指出的那样,您看到的行为是 as designed。
A better approach 是在单独的 class 中组织您的自定义,然后根据特定测试场景的需要启用它们。
自定义对象实现 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
class:
public class AllPersonsAreNamedBenAndAreBornIn1900 : CompositeCustomization
{
public AllPersonsAreNamedBenAndAreBornIn1900()
: base(new AllPersonsAreNamedBen(),
new AllPersonsAreBornIn1900())
{
}
}
此时你可以简单地说:
fixture.Customize(new AllPersonsAreNamedBenAndAreBornIn1900());
但是,请记住在 Fixture
上应用自定义设置的顺序很重要:最后一个获胜并且可能 覆盖 之前的,正如@MarkSeemann 在评论中指出的那样。这也是by design.
因此,虽然您 可以 组合适用于 不同 类型的现有自定义,但在这种特殊情况下,因为两个自定义都针对相同的类型,您必须创建一个新的自定义项来封装 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)));
}
}
作为一般规则,保持您的自定义小而 集中 使您能够 reuse them 在不同的测试中,将它们组合以用于特定的测试场景。
如已接受的答案所述,自定义将相互覆盖,因此您必须为要使用的每个属性组合编写自定义。
我编写了一个库,允许您编写自定义项,这样它们就不会相互覆盖,而是合并在一起。这将允许您对 People named Ben
进行一项自定义,对 Born in 1900
进行一项自定义,并且您可以将它们与 Compose 函数组合在一起。
它并没有完全解决原始问题,但它可能会给您一些关于如何实现您想要的想法。 Compose 功能是通过创建处理自定义的自定义 IFixture 实现来完成的。
https://github.com/LethargicDeveloper/Lift.AutoFixture
编辑:我看到 "active 4 months ago." 我错过了“3 年”。很抱歉复活这么旧的 post,但希望这会有用。