假设我有这个具体类:
public partial class User
{
public int ID { get; set; }
public string Email { get; set; }
public string FullName { get; set; }
}
我想创建一个匿名实例,它有一个有效的电子邮件地址,全名字段不超过 20 个字符。我可以这样做:
var fixture = new Fixture();
var anonUser = fixture.Build<User>()
.With(x => x.Email, string.Format("{0}@fobar.com", fixture.Create<string>()))
.With(x => x.FullName, fixture.Create<string>()Substring(0,20))
.Create();
有没有一种方法可以在一个地方定义它,以便 AF 知道我可以使用以下方法获取自定义的匿名类:
var newAnon = fixture.Build<User>();
最佳答案
您有多种选择。在我看来,最好的选择是应用 GOOS 听你的测试的原则。当测试变得难以编写时,就该重新考虑被测系统 (SUT) 的设计了。 AutoFixture 倾向于放大这种效果。
重构为值对象
如果您要求 Email
和 FullName
属性应该有特别限制的值,它可能表示而不是 Primitive Obsession ,目标 API 将受益于显式定义 Email
和 FullName
值对象。 canonical AutoFixture example is about phone numbers .
使用数据注释
您还可以使用 data annotations为 AutoFixture 提供有关值约束的提示。并非所有数据注释属性都受支持,但您可以同时使用 MaxLength和 RegularExpression .
它可能看起来像这样:
public partial class User
{
public int ID { get; set; }
[RegularExpression("regex for emails is much harder than you think")]
public string Email { get; set; }
[MaxLenght(20)]
public string FullName { get; set; }
}
就我个人而言,我不喜欢这种方法,因为我更喜欢 proper encapsulation相反。
使用自定义
而不是使用 Build<T>
方法,使用 Customize<T>
方法:
var fixture = new Fixture();
fixture.Customize<User>(c => c
.With(x => x.Email, string.Format("{0}@fobar.com", fixture.Create<string>())
.With(x => x.FullName, fixture.Create<string>().Substring(0,20)));
var newAnon = fixture.Create<User>();
编写约定驱动的标本生成器
最后,你可以写一个convention-driven customization :
public class EmailSpecimenBuilder : ISpecimenBuilder
{
public object Create(object request,
ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi == null)
{
return new NoSpecimen(request);
}
if (pi.PropertyType != typeof(string)
|| pi.Name != "Email")
{
return new NoSpecimen(request);
}
return string.Format("{0}@fobar.com", context.Resolve(typeof(string)));
}
}
我非常喜欢这种方法,因为我可以在此处放置任意复杂的逻辑,因此不必创建大量一次性自定义,我可以用一小组约定来驱动整个测试套件。这也往往会使目标代码更加一致。
关于c# - 将 DRY 应用于 Autofixture "Build"语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22331791/