将DRY应用于自动混合"构建"语句

Whi*_*uit 12 c# autofixture

假设我有这个具体的类:

public partial class User
{
    public int ID { get; set; }
    public string Email { get; set; }
    public string FullName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个具有有效电子邮件地址的匿名实例,并且fullname字段不超过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();
Run Code Online (Sandbox Code Playgroud)

有没有办法可以在一个地方定义它,所以AF知道我可以通过使用以下方式获得我的自定义anon类:

var newAnon = fixture.Build<User>();
Run Code Online (Sandbox Code Playgroud)

Mar*_*ann 12

你有各种选择.在我看来,最好的选择是应用GOOS原则来听你的测试.当测试变得难以编写时,是时候重新考虑被测系统(SUT)的设计了.AutoFixture倾向于放大这种效果.

重构对象值

如果您要求EmailFullName属性应具有特别受限的值,则可能表明目标API将受益于定义显式和值对象,而不是原始性.该规范AutoFixture例子是关于电话号码.EmailFullName

使用数据注释

您还可以使用数据注释来提供有关值的约束的AutoFixture提示.并非所有数据注释属性都受支持,但您可以同时使用MaxLengthRegularExpression.

它可能看起来像这样:

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; }
}
Run Code Online (Sandbox Code Playgroud)

就个人而言,我不喜欢这种方法,因为我更喜欢适当的封装.

使用自定义

而不是使用该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>();
Run Code Online (Sandbox Code Playgroud)

编写一个约定驱动的Specimen Builder

最后,您可以编写一个约定驱动的自定义:

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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

我非常喜欢这种方法,因为我可以在这里放置任意复杂的逻辑,因此我不需要创建大量的一次性自定义,而是可以使用一小组约定来驱动整个测试套件.这也倾向于使目标代码更加一致.